前言
作为一名小白,了解了基本的指针,在此,我将我在学习指针过程中,遇到的重难点分享出来。
(一)指针运算
指针运算一般运用于数组。因为数组在内存中是连续存放的,这时候进行指针的运算才有意义
1.指针变量与整型运算
下面以整型数组为例(代码有省略)
int arr[6] = { 1,2,3,4,5,6 };
取出首元素地址存入到指针变量p中
int* p = &arr[0];
一下代码在某次运行后得到了注释中的结果
printf("%p\n", p); //000000BB53AFF848
printf("%p\n", p + 1);//000000BB53AFF84C
printf("%p\n", p + 2);//000000BB53AFF850
printf("%p\n", p + 3);//000000BB53AFF854
由此可见,直接对指针变量p(int*类型)进行+1的操作,会得到p所存地址+4个字节后的地址。
那么,假如指针变量p是char*类型,p+1所得结果也是p所指向对象的地址加上4个字节吗?
初学时,我也有这样的误解,因为无论是char*还是int*这样的指针变量,它们在内存中的大小都是4或8个字节p+1结果为,p所指向对象的地址加上4个字节似乎有点道理。
char str[] = "abcdef";
char* p = &str[0];
printf("%p\n", p); //000000186514FA84
printf("%p\n", p + 1);//000000186514FA85
printf("%p\n", p + 2);//000000186514FA86
printf("%p\n", p + 3);//000000186514FA87
为了验证,我用char*类型的指针变量p做了实验,当指针变量p+1,结果是p所指向对象的地址+1。
由此,可以推断出结论,一个指针变量+1,结果与指针变量的类型有关。char类型是一个字节,那么char*指针变量+1结果就加上1个字节,int类型是4个字节,int*指针变量+1结果就加上了4个字节。
2.指针变量与指针变量运算
指针变量与指针变量相减会得到什么结果?
还是用数组举例。
指针与指针变量相减,相当于两个地址相减,若用整型来返回他们相减的值,过程如下
int arr[6] = { 1,2,3,4,5,6 };
int* p1 = &arr[1];
int* p2 = &arr[2];
printf("%d", (int)( & arr[1] - &arr[2]));//-1
可见,数组中两个元素的地址相减,返回整型,结果的绝对值,为它们所相差的元素个数。
3.指针变量大小比较
指针变量也可以比较大小
int arr[6] = { 1,2,3,4,5,6 };
int* p1 = &arr[1];
int* p2 = &arr[2];
if (p1 > p2) printf("1");
else printf("2");
//结果为2
指针变量比较大小,即比较地址的大小
(二)指针数组&数组指针
1.指针数组
指针数组,与整型数组,字符数组等其它数组一样,是一种数组。每个元素的类型是指针。
int arr1[3] = { 1,2,3 };
int arr2[3] = { 2,3,4 };
int arr3[3] = { 3,4,5 };
int* p[3] = { arr1,arr2,arr3 };
如图所示,p就是一个数组指针,里面有三个元素,存放着三个整型数组的首元素地址,即这三个元素的元素类型都是int*。
打印三个整型数组里的所有元素就可以通过这个指针数组来实现
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
p[i][j]看起来有点眼熟,很像二维数组的一个元素,实际上,通过p[i]访问到了三个数组的地址,数组地址再加上[j],就可以访问三个数组中的各个元素。所以,通过指针数组的方式,模拟出了二维数组。
2.数组指针
数组指针,与整型指针,字符型指针一样,都是一种指针。
定义
int arr1[3] = { 1,2,3 };
int arr2[3] = { 2,3,4 };
int arr3[3] = { 3,4,5 };
int(*p)[3] = &arr1;
int* p[3] = { arr1, arr2, arr3 };
为了方便比较,在此,我把数组指针,与指针数组都拿了出来。
不难发现,先用圆括号()把*p括起来,说明p是一个指针变量,指向的类型为int [3],即一个数组。
而不用圆括号()时,p会先于方括号[]结合,此时p就成了一个数组名,该数组每个元素的元素类型为int*类型。
数组指针有什么作用呢?
作用
void f1(int a[][3])
{
}
void f2(int(*p)[3])
{
}
int main()
{
int a[3][3] = { {1,2,3},{2,3,4},{3,4,5} };
f1(a);
f2(a);
return 0;
}
在上面的代码中,f1后的圆括号()中放的是二维数组的数组名,传过去的参数为二维数组的首元素地址,即一整个一维数组a[0]的地址,该地址的类型就是数组指针类型。
所以我们可以将函数的参数部分写成f2的形式,即用一个数组指针接收实参。
(三)函数指针&函数指针数组
1.函数指针
在了解函数指针前,我们先回顾一下数组指针
int arr1[3] = { 1,2,3 };
int(*p)[3] = &arr1;
数组指针与数组定义时有相通之处,即把定义时的数组名替换为*p
那么函数指针也是同样的道理
定义
int f1(int x)
{
return x + 1;
}
int main()
{
int (*p)(int) = &f1;
return 0;
}
把定义函数时的函数名换为(*p),此时的p就是一个函数指针变量
调用
int f1(int x)
{
return x + 1;
}
int main()
{
int (*p)(int) = &f1;
printf("%d\n", (*p)(5));//6
printf("%d\n", p(5)); //6
return 0;
}
无论是*p还是直接使用p,都能调出函数的地址,实际上,&f1与f1也都是函数的地址
2.函数指针数组
定义
int f1(int x)
{
return x + 1;
}
int f2(int x)
{
return x - 1;
}
int f3(int x)
{
return x;
}
int main()
{
int (*p[3])(int) = { &f1,&f2,&f3 };
return 0;
}
在p后加上方括号[],此时的p就是一个函数指针数组的数组名
每个元素的类型为函数指针int(*)(int)
调用
int f1(int x)
{
return x + 1;
}
int f2(int x)
{
return x - 1;
}
int f3(int x)
{
return x;
}
int main()
{
int (*p[3])(int) = { &f1,&f2,&f3 };
for (int i = 0; i < 3; i++)
{
printf("%d ", p[i](5));//6 4 5
}
return 0;
}
以i=1为例,p[1]调用出了函数地址(函数名),再加上(5),就相当于使用了f1(5),即调用了函数。
(四)回调函数
回调函数,指的是一个函数a,它的地址以函数指针变量的形式被传给了函数b,当函数b中的函数指针变量被调用时,该函数指针变量所指向的函数a就是回调函数。
int a(int x)
{
return x + 1;
}
void b(int (*p1)(int))
{
int ret = a(5);
printf("%d\n", ret);
}
int main()
{
int (*p)(int) = &a;
b(p);
return 0;
}
如图就是一个例子
(五)总结
该博客介绍了指针基本相关的一些易混淆的概念。到此结束,谢谢浏览