目录
指针的进阶
前言
上文简易指针中我们介绍了一些常见的简单指针的用法
这期,咱们来学习进阶版的指针用法。
1. 字符指针
其中*p指向的并不是把 “abcdef” 放到了指针p中,而是将首字符的地址放到了指针p中。这是很多人常犯的错误。
正是因为这个性质,在打印单个字符的时候,指针需要解引用,而用 %s 打印整个字符串的时候,只需要传首元素地址即可,而指针刚好指向的是首元素地址。
曾经的一道面试例题:
答案是:......not same .......same
原因:前面两个字符数组,是把其中的字符放入到了str1、str2中,这两个地址是不同的。所以他们不同。而指针str3、指针str4则是指向同一块内存中存放的字符串,所以他们是相同的。
2. 数组指针
顾名思义,这是一种指针,并且是一种可以指向数组的指针。
那么他是哪种形式的呢?我们知道
int* arr[10];
由于 [ ] 的的优先级比 * 的要高,所以,数组名是优先和 [ ] 结合的,这样就是指针数组了。
因此,我们应该确保 * 和数组名优先结合,形式如下:
int (*arr)[10];
&数组名和数组名的区别
我们知道数组名代表的是数组首元素的地址,那么&数组名代表了什么呢?我们看这段代码:
#include <stdio.h> int main() { int arr[10] = {0}; printf("%p\n", arr); printf("%p\n", &arr); return 0; }
运行结果如下:
可以看到结果是一样的,但是真的一样吗?
#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; }
这段代码的结果就很明显
实际上:&数组名代表的是整个数组的地址,当数组的地址+1的时候,跳过的是整个数组的大小。所以差值是40了
数组指针应用于二维数组的实例
3. 指针数组
前文我们已经讲过,指针数组是一个存放指针的数组。
4. 数组传参和指针传参
当传入的是数组名的时候,函数参数无论是数组、指针、还是指针 [ ] 都是可以的
当传入的是指针数组的数组名,那么这时候就要用到二级指针了,指向指针的地址了。
二维数组:
在用二维数组传参的时候,和之前初始化的时候一样,列数是一定要给的,不然就是错误的。
而 int *arr [ ] 错误的原因是这是一个指针数组,里面的元素都是指针,所以肯定是错误的。
int *arr 错误是因为二维数组的首元素是一个行,用一个指针来是接收不了一行的内容的。
用二级指针接收肯定也是,错误的,因为传来的是元素的地址,而元素是整形。
5. 函数指针
函数指针是用来存放函数的地址的一种指针。
形式为:返回值类型+(*数组名)(函数参数类型)
void (*pf)(int , int);
为什么要用()让*和函数名优先结合呢?原理是和数组指针相同的,因为()的优先级更高,会优先和参数结合,这样就变成函数了。
其中参数的名字写不写无所谓的。
在使用的时候,这个*是可有可无的,图中划横线的两行效果一样。甚至你可以用******都是可以的。
6. 函数指针数组
一个数组中存放的全是函数指针,那么这个数组就是函数指针数组。
上面代码可以看出。
为什么要把 [ ] 放在括号里面呢?因为他的性质是一个数组,所以名字是要和 [ ] 先结合的。
7. 指向函数指针数组的指针
最下面这一行就是指向函数指针数组的指针。
怎么理解这一行呢?我们先拆解他
他首先是一个(*ppfarr),那么他的性质是一个指针,然后外面是 [ ] 其次他的性质是一个数组,然后再和外面的*结合,那么我们可以理解为,这是一个指针,指向一个数组,数组里面存放的是函数的指针。