持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天点击查看活动详情
指针
//在栈中申请了一个int指针变量a a有他自己的地址存储在栈中
//然后new int是在堆中申请了一个内存 用a存储 而栈中地址存储的就是new int申请的内存地址
int* a = new int;
cout << "a:" << a << " " << "&a:" << &a << endl;
//在堆中 a存储的值 在栈中 是a的地址
指针指向的地址实际上是一个虚拟地址 进程结束后就会释放所有的内存空间 操作系统会进行映射
nullptr(空指针)
nullptr和NULL,0的区别是在逻辑判断的时候能知道这是一个空指针
int* a = new int;
delete a;//删除之后a存储的值其实并不是0或者NULL 是一个其他的值
//a = nullptr;//如果不写这个就会执行1
if (a)//1
{
cout << "&a:" << &a << endl;
cout << "a:" << a << endl;
//在堆中 a存储的值 在栈中 是a的地址
}
else//2
{
cout << "NULL" << endl;
}
为什么要指针类型
32位中任何类型的指针都是四个字节 64位是八个字节 例如int a;chaa 那么他们有什么区别呢 指针只是指向了一个内存地址,但是当从内存中取值的时候,系统不知道你要从当前指针指向的地址,取几个字节,指定了指针的类型后,系统就知道取几个字节了。char类型取1个字节,short类型取2个字节,int类型取4个字节。 定义了类型也会决定他的操作方式 指针移位方式 取值方式
函数指针
本质是指针 指向一个函数 函数的定义是存在于代码段,因此,每个函数在代码段中,也有着自己的入口地址 函数指针就是指向这个入口地址
例子
int fun(int b)
{
return b;
}
void main()
{
int b = 5;
int(*a)(int) ;
a = fun;
cout<<a(b);
}
void fun()
{
cout << "fun" << endl;
}
void main()
{
void(*a)() ;
a = fun;
a();
}
/*如何声明函数指针*/
//void (*pf)();//就是正常的声明一个函数一样的加了一个*号
int *fun(int b)//这是一个返回值为int*类型的函数
{
//cout << "salfh";
int* a = new int;
*a = b;
return a;
}
void main()
{
int* (*pf)(int);//指向返回值为int型指针的函数指针
pf = fun;
cout << (*pf)(10) << endl;//地址 这里调用的就是指针函数 返回了返回值(指针指向的地址)
cout << *(*pf)(10) << endl;//10 这里返回的就是指针里面存储的值了
/*函数指针数组*/
int* (*pa[2])(int) = {pf,nullptr};//指向返回值为int型指针的函数指针数组
cout << *(*pa[0])(10) << endl;//10
}
将函数指针作为函数形参
int prin(int a, int b)
{
cout << "a:" << a << endl;
cout << "b:" << b << endl;
return 0;
}
int fun(int a, int b,int(*func)(int, int))
{
func(a,b);
return 0;
}
void main()
{
int a = 1;
int b = 2;
fun(1, 2, prin);
}
回调函数
int func_sum(int n)
{
int sum = 0;
if (n < 0)
{
printf("n must be > 0\n");
exit(-1);
}
for (int i = 0; i < n; i++)
{
sum += i;
}
return sum;
}
//这个函数是回调函数,其中第二个参数为一个函数指针,通过该函数指针来调用求和函数,并把结果返回给主调函数
int callback(int n, int (*p)(int))
{
return p(n);
}
int main(void)
{
int n = 0;
printf("please input number:");
scanf("%d", &n);
printf("the sum from 0 to %d is %d\n", n, callback(n, func_sum)); //此处直接调用回调函数,而不是直接调用func_sum函数
return 0;
}
指针函数
就是返回值是指针的函数
指针数组
int *p[];//这个需要看成 p变量是一个数组 是一个int*类型的数组 所以他每个元素应该存储int*变量
int* a[2];//存储了两个指针的数组 这个*是和int结合
int* b;
int c = 10;
b = &c;
a[0] = b;
cout << *(a[0]);
简便用法
int *pnum(&num1);//将num1的地址传递给pnum
int * &rnum = pnum;//rnum是pnum的别名 实际上就是在int*rnum = pnum的基础上加了个引用符号
rnum = &num2;//rnumhe pnum指向同一片内存 改变了rnum就相当于改变了pnum
关于* a++和 *(a+1)
a是一个数组名 int a[5];
*a++中 ++优先于 *所以这个++是对a地址进行+1 这是不被允许的 数组a = a+1是不行的 但是 int*b = a; b++;是可以的
而*(a+1)则并没有改变a的值 只是取了a前一位地址的值 也就是a[1]
指针+1
int (*a)[10] 和 int *a[10]的区别
a是优先和[10]结合
int *a[10]
int* a[10] 很明显就是a[10]是一个数组 而数组里面的元素都是int*类型
int* d1, * d2, * d3;
int* d[3] = { d1,d2,d3 };
int (*a)[10]
首先是(*a)是和[10]先结合 那么 我们看最前面可以知道[10]里面存储的是10个int 那么就是 a[10] 和 a[10]的区别 很明显a[10]是一个指针 指向这个存储10个int的数组
//int(*a)[3] = {3,5,7};//这样是不对的
int b[3] = {3 ,5,7};
//int(*a)[3] = b;//不合法
int(*a)[3] = &b;//和int*a = &b区别很大 这个a是指向一个数组 很像一个 int a[1][3]这样的数组
//int b[4];
//int(*a)[3] = &b;//这样会报错 一定只能指向包含三个元素的数组 定死了 四个也不行
int* c = b;
cout << *a + 1;//是一个地址
cout << **a + 1;//4
cout << c+1;//是一个地址
cout << *c+1;//4
数组(常量指针)
数组名是指针常量,指针是指针变量
常量指针是指向常量的指针 也就是他指向的地址存储的值不能改变 指针常量是指针是一个常量 是不能改变指向的
数组名是一个指向数组首元素地址的常量指针 数组名一经定义就没办法改变
int a[3] = {3,5,7};
cout << "a:" << a << endl;//00F3F790
cout << "&a:" << &a << endl;//00F3F790
cout << "&a[0]" << &a[0] << endl;//00F3F790
cout << "*a:" << *a << endl;//3
系统在使用数组下标对数组成员变量进行访问时,开销比较大,指针的访问效率是远远大于数组名的访问效率的
局部性原理
cpu会将一部分连续的地址传入高速缓冲区 所以数组比链表更快些