前言
- 首先我们要知道什么是指针,指针就是个变量,用来存放地址,地址唯一标识一块内存空间。指针的大小是固定的4/8个字节(32位平台/64位平台),并且指针是有类型,指针的类型决定了指针的 ± 整数的步长,指针解引用操作的时候的权限,因此我们以不同类型的指针展开分开讲解。
1.字符指针
- 字符指针的模样是char* ,通常用来存储字符或字符串的地址,比如:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
- 这其实就是将字符‘w’的地址存放到了指针变量pc当中,当使用它的时候我们只需要对它进行解引用操作就可以了,解引用操作符是 * ,在上面的 *pc当中, *pc = = ch = = ’ w ',这里只是说等价,不是 == 操作符。上面也说它还可以存储字符串的地址,像这样:
int main()
{
const char* pstr = "hello world";
printf("%s\n", pstr);
return 0;
}
- 那我们可以思考这是将“ hello world " 的全部地址都存放到指针变量pstr当中吗?其实并不是的,它存放的只有首字符‘ w ’ 的地址,但是它与数组相似,会根据第一个元素的地址依次找到后面的元素,从而将它们打印出来。
2.指针数组
- 这个也是很好理解,比如int arr[10] 是整型数组,arr是数组名,存放的元素是整型,所以是整型数组。那么指针数组也是一样,首先是个数组,其次存放元素是指针,写出来就是int* arr[10] ,这里也就是整形指针数组,当然还有char* arr[10],我就不一一列举了。
3.数组指针
- 那我们前面讲了指针数组,指针数组是指针呢?还是数组呢?根据上面的讲解我们知道指针数组是一个数组,那么同样的理解数组指针应该就是一个指针。
- 那么数组指针又是什么样的呢?我们最熟悉的是整型指针,比如int* pc,我们一步一步来分析,首先它是个指针,写法为* pc,其次指向的是整型,那么就是int* pc。数组指针也可以这么去分析,首先是个指针* ptr,再然后指向的是一个数组,那么就是 (*ptr)[10],每个数组的元素类型是int,那么最终的写法就是int (*ptr)[10]。
- 至于这里为什么带括号,是因为‘ [ ] ’ 的优先级比‘ * ’ 要高,如果不带括号ptr就会先于[ ] 结合。那么ptr就不在是指针了,ptr[ ]就变成了一个数组,存放的元素为整型指针,从而变成了指针数组int* ptr[10],而不是数组指针,因此必须加上()来保证p先和*结合。
&数组名与数组名
- 这里我们在额外讲解一些知识,我们都是数组名是首元素的地址,那么&数组名与数组名一样吗?我们来看看以下代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
- 如果&数组名与数组名是相等的话,那么打印出来的结果是不是一样的呢?让我们来看结果:
- 我们发现arr与&arr的地址是一样的,说明都是首元素的地址,但是都+1之后的结果为什么不同呢?我们会发现006FF9C4与006FF9C8之间差了一个4,刚好是一个int的大小,说明arr+1只是跳过了一个元素大小,那我们看006FF9C4与006FF9EC,相差了28,(注意:这里的地址都是用16进制表示的)将十六进制28转化为十进制是(216 ^ 1 + 816 ^ 1) = 40,算法与二进制转十进制一样,只是把10换成了16。40刚好是10个整型的空间,说明跳过的刚好是一个数组的大小,此时我们就知道了arr是首元素的地址,而&arr取出的是整个数组的地址。
- 根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义是不一样的。实际上:&arr 表示的是数组的地址,而不是数组首元素的地址。
4.函数指针
- 顾名思义,它本质上还是一个指针,但是指向的是函数,那我们就先举个例子把,比如int Add(int x,int y),那么这又该如何表示呢?我们可以按照之前的思路,首先它是一个指针*pf,然后指向的是函数,那么就是(*pf)(),而每个函数的参数是不一样的,因此我们在书写的时候也要带上参数的类型,那么就是(*pf)(int,int),再然后就是函数的返回类型是int,那么我们的函数指针就是int (*pf)(int,int),使用的话也很简单,只需要 int ret = (*pf)(5,7)就可以了,其实这个 * 写不写都可以,此处写上只是方便理解。
- 那就让我们看一个代码:(* (void ( * ) ( ) ) 0 )( );这个代码是什么意思呢?突破口当然是0了,就它不一样是个数字,那我们就来看 0 的前面是什么,以一个括号来看前面是:( void( * )( ) ),最外面的这一层括号其实是强制类型转换,意思就是将0的这个地址强制类型转换成了void( * )( ),它是一个函数指针,参数为空,返回类型为void,也就是在0这块地址空间被换成了一个函数指针,在0这里进行调用。
5.函数指针数组
- 一样的解读思路,它是一个数组,存放的元素类型是函数指针,这里就不多讲解了,它的书写方式是int (* pf[10] )( int ),这样就是一个函数指针数组,是一个数组,存放了10个函数指针。
- 那么这有什么用呢?当我们些一些相似的函数时,就可以使用函数指针数组,这可以极大的方便我们的书写,我们来看代码:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf( "*************************\n" );
printf( " 1:add 2:sub \n" );
printf( " 3:mul 4:div \n" );
printf( "*************************\n" );
printf( "请选择:" );
scanf( "%d", &input);
if ((input <= 4 && input >= 1))
{
printf( "输入操作数:" );
scanf( "%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
printf( "输入有误\n" );
printf( "ret = %d\n", ret);
}
return 0;
}
- 如果正常写的话我们可能会用switch结构来进行选择,并且每次都要写出提示用户选择哪一个操作,这样用函数指针数组的话就可以极大的快捷了,并且也方便书写。
6.指向函数指针数组的指针
-
它是一个指针,指向一个数组,数组存放的元素类型时函数指针,这就是指向函数指针数组的指针。
-
首先是一个指针:*pf
-
然后指向数组:(*pf)[10]
-
再然后存放的是函数指针: int ( (*pf)[10] )( int ) 或 int ( * (*pf)[10] )( int ),这个‘ * ’写不写都可以的,上面有提到过,那就看看是如何定义的吧。
void test(char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针 pf
void (*pf)(char*) = test;
//函数指针的数组 pfArr
void (*pfArr[5])(char* str);
pfArr[0] = test;
//指向函数指针数组 pfArr的指针 ppfArr
void (*(*ppfArr)[5])(char*) = &pfArr;
return 0;
}
总结
- 好耶!!又双叒叕完成了一篇文章,此次文章还没写完,暂时先写这么多啦,如果大家有什么疑问可以私信我哦,我会为大家解答并且把大家的问题在本文中详细的写出来的!!
- 希望这篇文章能对大家对指针的理解更深一层,如果觉得不错👍,可以给博主一个免费的一键三连,点赞、收藏+关注,谢谢大家啦!!如果有什么疑问或者发现了文章中的问题,可以在评论区留言喔。希望大家都会有所收获喔!!