C++指针进阶

指针

数据类型 *p这个为指针变量,实际上指针变量所指向的就是变量的地址
解引用*p,我们可以通过解引用*p来改变指针所指向的地址中的变量值
例如:

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int a=3;
	int *p=&a;
	cout<<&a<<" "<<p<<endl;
	cout<<a<<" "<<*p<<endl;
	*p=8;
	cout<<a<<" "<<*p<<endl;
} 

在这里插入图片描述

动态分配内存new和delete

使用堆区内存的有四个步骤:

  • 声明一个指针;
  • 用new运算符向系统申请一块内存,让指针指向这块内存;
  • 通过对指针解引用的方法,像使用变量一样使用这块内存;
  • 如果这块内存不用了,用delete运算符释放它;
    申请内存的语法:new 数据类型(初始值);//c++11支持{}
    如果申请成功,返回一个地址;如果申请失败,返回一个空地址(暂时不考虑失败的情况)。
    释放内存的语法:delete 地址;
    例如:
    我们知道一个申请一个指针*p,一定要给他赋初始值,这个初始值要是一快地址,我们可以申请一块。
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int *p=new int(5);//申请一块内存,并给这个内存赋初始值5 
	cout<<"*p="<<*p<<endl;
	delete p;
}

注意:

  • 动态分配出来的内存没有变量名,只能通过指向它的指针来操作内存中的数据。
  • 如果动态分配的内存不用了,必须用delete释放它,否则有可能用尽系统的内存。
  • 动态分配的内存生命周期与程序相同,程序退出时,如果没有释放,系统将自动回收。
  • 就算指针的作用域已失效,所指向的内存也不会释放。
  • 用指针跟踪已分配的内存时,不能跟丢。

二级指针

指针指针变量的简称,也是变量,是变量就有地址
指针用于存放普通变量地址
二级指针用于存放指针变量地址
声明二级指针的语法:数据类型**指针名
使用指针有两个目的:

  • 传递地址
    • 存放动态分配的内存的地址。
      在函数中,如果传递普通变量的地址,形参用指针;传递指针的地址的地址,形参用二级指针。
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int ii=8;
	cout<<"ii="<<ii<<",ii的地址是:"<<&ii<<endl;
	int *pii=&ii;
	cout<<"pii="<<pii<<",pii的地址是:"<<&pii<<",*pii="<<*pii<<endl;
	int **ppii=&pii;
	cout<<"ppii="<<ppii<<",ppii的地址是:"<<&ppii<<",*ppii="<<*ppii<<endl;
	cout<<"**ppii="<<**ppii<<endl; 
} 

二维指针函数

#include<iostream>
#include<algorithm>
using namespace std;
void func(int **p){
	*p=new int(3);
	cout<<"p="<<p<<",*p="<<*p<<endl;
}
int main(){
	int *p=0;
	func(&p);
	cout<<"p="<<p<<"*p="<<*p<<endl;
} 

空指针

在C和C++中,用0或 NULL都可以表示空指针。
声明指针后,在赋值之前,让它指向空,表示没有指向任何地址。

使用空指针的后果

如果对空指针解引用,程序会崩溃。
如果对空指针使用delete运算符,系统将忽略该操作,不会出现异常。所以,内存被释放后,也应该把指针指向空。

野指针

野指针就是指针指向的不是一个有效(合法)的地址。
在程序中,如果访问野指针,可能会造成程序的崩溃。
出现野指针的情况主要有三种:

  • 指针在定义的时候,如果没有行初始化,它的值是不确定的((乱指一气)。
  • 如果用指针指向了动态分配的内存,内存被释放后指针不会置空,但是,指向的地址已失效。
  • 指针指向的变量已超越变量作用域(变量的内存空间已被系统回收)。

规避方法:

  • 指针在定义的时候,如果没地方指,就初始化为nullptr。
  • 动态分配的内存被释放后,将其置为nullptr。e
  • 函数不要返回局部变量的地址。
    注意:野指针的危害比空指针要大很多,在程序中,如果访问野指针,可能会造成程序的崩溃。是可能,不是一定,程序的表现是不稳定,增加了调试程序的难度。

函数指针和回调函数

函数的二进制代码存放在内存四区中的代码段,函数的地址是它在内存中的起始地址。如果把函数的地址作为参数传递给函数,就可以在函数中灵活的调用其它函数。
使用函数指针的三个步骤:

  • 声明函数指针;
  • 让函数指针指向函数的地址;
  • 通过函数指针调用函数。
  1. 声明函数指针
    声明普通指针时,必须提供指针的类型。同样,声明函数指针时,也必须提供函数类型,函数的类型是指返回值参数列表(函数名和形参名不是)。
    假设函数的原型为:

int func1(int bh,string str);
int func2(int no,string message);
int func3(int id,string info)
bool func4(int id,string info)
bool func5(int id)
则函数指针的声明为:
int (*pfa)(int,string);
bool (*pfb)(int,string);
bool (*pfc)(int);

例如:

#include <iostream>//包含头文件。
using namespace std;//指定缺省的命名空间。
void func(int no, string str){
	cout<<"亲爱的"<<no <<"号: "<< str << endl;
}
int main(){
	int bh = 3;//超女的编号。
	string message ="我是一只傻傻鸟。";//向超女表白的内容。
	func(bh, message);
	void(*pfunc)(int,string);//声明表白函数的函数指针。
	pfunc = func;//对函数指针赋值,语法是函数指针名=函数名。
	pfunc(bh, message);//用函数指针名调用函数。C++
	(*pfunc)(bh, message);//用函数指针名调用函数。C语言  
}

那么这个函数指针有什么用呢?
对于这个函数指针,我们可以设想这个情景,一个表白成绩,假设表白的场地什么的都布置好了,但是表白还得你自己做。

void 个性化表白函数(){
	//个性化表白代码
}
void 表白神器(个性化表白函数指针 p){
	//表白之前的准备工作。
	个性化表白函数指针p();
	//表白之后的收尾工作
}
int main(){
	表白神奇(个性化表白函数名);
}

例子:这个就是函数的参数是个函数

#include<iostream>
#include<algorithm>
using namespace std;
void zs(int a){//张三个性化表白函数 
	cout<<"a="<<a<<"我要先翻三个跟头在表白\n";//个性化表白函数 
}
void ls(int a){//张三个性化表白函数 
	cout<<"a="<<a<<"我要先翻四个跟头在表白\n";//个性化表白函数 
}
void show(void (*pf)(int),int b){
	cout<<"表白之前的准备工作已完成。\n";
	pf(b);
	cout<<"表白之后的收尾工作已经完成。\n"; 
}

int main(){
	show(zs,3);//张三表白函数 
	show(ls,4); //李四表白函数 
}
  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值