绪论
书接上回,通过对数据类型进阶的认识,你肯定对各种数据类型在内存中如何存储有了了解。虽然说,这方面可能对你的编程能力没什么进步。但是,他是一本内功秘籍,当我们遇到了这方面的问题时我们可以知道可能是哪一方面出了问题。本章进阶指针,可能会有更多的知识点和更复杂的问题,但其实只要你对初阶的知识有很好的认识他也是小ks。
最后祝大家新年快乐,万事如意!
所以安全带系好,发车啦(建议电脑观看)。
思维导图:
要XMind思维导图的话可以私信哈
目录
1.字符指针
2.指针数组
2.1指针数组的定义和使用方法
3.数组指针
3.1 定义
3.2 &数组名与数组名
3.3数组指针的使用
4.数组传参、指针传参
4.1 一维数组和指针数组传参
4.2 二维数组传参
4.3一级指针传参
4.4二级指针传参
5.函数指针
5.1.1函数指针的创建
5.1.2函数指针的使用
6.函数指针数组
6.1函数指针数组定义
7.指向函数指针数组的指针
8.回调指针
8.1.1回调函数的定义:
1.字符指针
1.1
知识点:
字符指针常见的的使用情况:
int main() { char a = 'a'; //字符指针 //1. char * pa = &a; //2. char * pa = "abcd"; return 0; }
在第一种情况时:是直接将a的地址存进了一个指针中这个指针叫字符指针
在第二种情况时:是将字符串"abcd"中的首元素的'a'字符的地址存进一个字符指针中 (在常量字符串中他产生的值就是'a'字符的地址即"abcd" == &'a' )
细节(注意点):
常量字符串不能被修改、两相同的常量字符串完全相同
即当char * pa= "abcdef"时
*pa = 'w'(这样时错误的)
再如:
char arr[] = "abcdef";
arr = "cdefg";
这样同样是不行的
练习:
#include<stdio.h> int main() { char arr1[] = "abcdef"; char arr2[] = "abcdef"; const char *arr3 = "abcdef"; const char *arr4 = "abcdef"; if(arr1 == arr2) printf("same\n"); else printf("no same\n"); if(arr3 == arr4) printf("same\n"); else printf("no same\n"); return 0; }
分析:
首先,有2个数组变量arr1 arr2存放着字符串,并且还有两个字符指针存放着常量字符串的首元素地址
其次,因为两个数组会创建两个不同的空间即就会导致其数组名表示的首元素地址不同;而对于arr3 和 arr4 来说虽然他们将‘a’字符的地址存放在不同的空间,但是其指针变量的变量名就表示其内存中所存的地址(3、4都常量字符串的首元素地址)
最后,通过上面标红的字就可以推出最终将打印 no same 、 same
附这里我们需要注意的是arr1 arr2 arr3 arr4 他们分别代表着意思
数组名:首元素的地址
指针变量的变量名:代表着存放的地址
2.指针数组
2.1指针数组的定义和使用方法
知识点:
指针数组,字如其名是一个存放了多个指针的数组
我们不妨那整形数组、字符数组对应了来看
而我们有知道指针(变量)使用用来存放地址的,所以我们在为int * arr[4] 初始化时应该存放地址进去
细节(具体):
对于指针数组的使用:
#include<stdio.h> int main() { char* arr[4] = { "abcd","hehe","blog","nb" };//常量字符串产生的是其首元素的地址 for (int i = 0; i < 4; i++) { printf("%s ", arr[i]); } return 0; }
练习:
通过指针数组来模拟实现二维数组:
为什么可以直接写成arr[][]的形式,是因为你看arr[ i ][ j ]第一个 arr[i] 就是找到了 存放在指针数组中各个数组的地址(即数组名) 当i为0时(arr[ 0 ] == arr1 ):arr1 [ j ] 是不是就是变成了数组的表示方法。
当然我们也可以吧arr [ i ] [ j ]表示成 *(arr[ i ] + j )
3.数组指针
3.1 定义
知识点:
同整形指针、字符指针一样他是一种存放数组的指针
整形指针:int * p;
字符指针:char * p;
不难发现他们是由 所指向地址的类型 + * (表示变量是一个指针) + 变量名 组成的(这点很重要会贯穿后面指针的内容次处称为指针的3步原则)
所以同理,数组指针他也是如此组成的:
int arr[ 4 ] = {1,2,3,4};
int (*parr)[ 4 ] = &arr;
此时的地址数组的类型为int [ 4 ] 、 *代表其为一个指针,加上括号是因为防止因为[ ] 的优先级比 * 的优先级高而导致变成 指针数组 、 再加上变量名 就成了数组指针
3.2 &数组名与数组名
知识点:
数组名在讲数组内容处已经提过为:这个数组的首元素的地址
&数组名:如int arr[ 10 ] = {0}; arr表示数组名,那么取地址的数组名就真正的表示这个数组
细节:
下面通过代码来更加详细的解释:
int main() { int arr[10] = { 0 }; printf("%p\n", &arr[0]);//首元素的地址 printf("%p\n", arr);//数组名 printf("%p\n", &arr);//整个数组的地址 return 0; }
此处可以发现他们三个的地址都相同,但是有什么不同地方呢?让我们继续往下
int main() { int arr[10] = { 0 }; printf("%p\n", &arr[0]);//首元素的地址 printf("%p\n", &arr[0]+1);//首元素的地址 printf("%p\n", arr);//数组名 printf("%p\n", arr+1);//数组名 printf("%p\n", &arr);//整个数组的地址 printf("%p\n", &arr+1);//整个数组的地址 return 0; }
此处当我们+1后,可以发现数组名的结果和&arr[0]的效果(加了4byte)是一样的都是,而&arr却加了40(70-48 == 28,十六进制不够接16,转化成十进制就是40byte)
所以,最终得出结论就是
arr(除了两种特别情况外)都表示首元素的地址
&arr 则直接表示整个数组
他们的不同处在于,代表的意义不一样
最终与上面所讲到的数组指针结合:
int arr[ 4 ] = {1,2,3,4};
int (*parr)[ 4 ] = &arr;
此处的数组指针和指针数组就有着明显的区别:
一个是存整个数组的地址的,一个是存数组中的各个数的地址的。
int * arr[4] = {arr,arr+1,arr+2,arr+3};
int (*p)[4] = &arr;
3.3数组指针的使用
直接通过代码注释的形式来展示;
//对于一维数组时 int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int(*p)[10] = &arr; for (int i = 0; i < 10; i++) { printf("%d ", (*p)[i]);//通过(*p)找到整个数组 * &arr等于arr再通过下标引用找到对应 } //而对于一维数组来说这样写反而有点冗余了,不如直接用指针*(p+i) return 0; } //对于二维数组来说 void test(int(*p)[4], int r, int c)//将二维数组的首元素传进其表示二维数组的第一行即一整个数组 { int i = 0; for (i = 0 ;i < r; i++)//i表示访问的行 { int j = 0; for (j = 0; j < c; j++) { printf("%d", (*(p + i))[j]);//访问第i行j列 //printf("%d", p[i][j]);p[i] == *(p+i) } printf("\n"); } } int main() { int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} }; test(arr, 3, 4); }
附:当在函数内接收一个二维数组时,方块内放到是二维数组的列宽
注意点:
数组指针*p = * &arr = arr
int (* parr[10])[5]: