这里我们注意一下 ** ptr,我们解引用一次得到p的地址,p的地址再解引用一次就得到n 也就是10。
二级指针我们知道,是用来接收一级指针地址的(注意指针变量的地址不要与指针变量中存储的地址搞混淆),那么一级指针地址有几种表示方法呢?
int n = 10; int*p = &n; int **pp = &p; test(pp); test(&p);
这里就是答案。
思考: 当函数的参数为二级指针的时候,可以接收什么参数
void test(int** ptr) { printf("%p\n", **ptr); } int main() { int a = 10; int* p = &a; int** pp = &p; test(&p); test(pp); int* arr[10] = { p }; test(arr); test(&arr[0]); return 0; }
我们从二级指针接收一级指针的地址
作为突破口,
那么除了这两种还有一类。
test(&p); test(pp);
那就是指针数组,指针数组的每个元素都是指针变量的地址,这样我们就能理解了。
函数指针🥶
我们都知道变量是有地址的,那么函数有地址吗?
肯定也是有点既然有地址那么就可以与指针有联系。
这就是我们将会介绍的函数指针。
我们来看一段代码:
void test() { printf("hehe\n"); } int main() { printf("%p\n", test);//直接打印函数名 printf("%p\n", &test);//取函数的地址打印 return 0; }
发现这两种写法其实是一样的。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:
//下面pfun1和pfun2哪个有能力存放test函数的地址? void (*pfun1)(); void *pfun2();
答:pfun1可以存放。
pfun1先和结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
我们已经学会了这么函数指针的一般表达方式,那么我们来尝试使用使用。
使用函数指针
int Add(int a, int b) { return (a + b); } int main() { int x = 5; int y = 3; printf("%d\n", Add(x, y)); int (*p1)(int, int) = Add; int (*p2)(int, int) = &Add; printf("%d\n", p1(5,3)); printf("%d\n", (*p2)(5, 3)); printf("%d\n", Add(5,3)); printf("%d\n", (&Add)(5, 3)); return 0; }
我们看到这几种写法都是可以的,
printf("%d\n", p1(5,3)); printf("%d\n", (*p2)(5, 3)); printf("%d\n", Add(5,3)); printf("%d\n", (&Add)(5, 3));
p1和p2都是存储Add函数的地址,而函数名就相当于函数地址(p1 | p2 == Add),那么解引用实际上有没有都一样,哪怕你写(********p2)都行,并没有什么太大的意义。
我们已经学会了函数指针的写法和用法接下来我们出两个题目。
//代码1 (*(void (*)())0)(); //代码2 void (*signal(int , void(*)(int)))(int);
我们不要畏惧这种代码其实只要我们用心加上经验就能看出来。
开始分析
(*(void (*)())0)();
首先我们能看到void ( * )( )
这个函数指针和0
我们看到( * )
里面没有写变量,
那么说明这是一种类型,而0是什么呢?其实这里的0指的是0地址(void ( * )( ))0
那么这一部分的意思就是将0地址的类型转换为void ( * )( )
这就意味着0地址处放一个返回类型是void,无参的一个函数
现在我们可以把代码简化一下变成:( * 0)( );
也就是说调用0地址处的函数刚好也
与我们void ( * )( )
相对应,只是我们不需要再写返回值,只需要写参数
这里刚好无参
这就是在表达调用0地址处的这个函数
void (*signal(int , void(*)(int)))(int);
我们还是先从内部找我们认识的,(int, void( * )(int))
这是一个函数的参数
第一个参数是int
,第二个参数是一个函数指针
那么signal
就应该是函数名
我们去掉这部分再观察void ( * )(int);
我们看到只剩下这一部分,还是一个函数指针类型,那这
是什么意思呢?其实是函数指针的返回值的表达形式
也就是说signal函数的返回值类型
也是:void( * )(int)的函数指针
这就是我们所讲解的函数指针类型。
函数指针数组🐣
上面我们讲解了函数指针,现在我们来看看函数指针数组又不同在哪一点
我们知道指针数组是这样的:
int *p[5] p先和[]结合,说明是数组然后,int * 为类型
那么函数指针数组呢?
其实只需要把类型变换一下 int (*parr[5])(int ,int)
parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。
然后我们看一下到底如何使用
int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int Mul(int x, int y) { return x * y; } int Div(int x, int y) { return x / y; } int main() { int (*parr[4])(int, int) = { Add,Sub,Mul,Div }; printf("%d\n", (parr[0](4, 2))); printf("%d\n", (parr[1](4, 2))); printf("%d\n", (parr[2](4, 2))); printf("%d\n", (parr[3](4, 2))); return 0; }
其实函数指针数组的用途:转移表
void menu() { printf("**************************\n"); printf("**** 1.add 2.sub ****\n"); printf("**** 3.mul 4.div ****\n"); printf("**** 0.exit ****\n"); printf("**************************\n"); } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; //转移表 int (*pfArr[])(int, int) = { 0, Add, Sub, Mul, Div }; do { menu(); printf("请选择:>"); scanf("%d", &input); if (input == 0) { printf("退出计算器\n"); } else if (input >= 1 && input <= 4) { printf("请输入2个操作数:>"); scanf("%d%d", &x, &y); ret = pfArr[input](x, y); printf("ret = %d\n", ret); } else { printf("选择错误\n"); } } while (input); return 0; }
为什么要这样呢?
如果我们不使用转移表那么就会写出多个case
,这样做可以减少代码的冗余,简单明了。
指向函数指针数组的指针🦄
这也是我的博客中指针类型的最后一种,我们看到标题的最后是什么什么的指针,那么说明就是一个指针只不过类型复杂了那么一点。
int Add(int x, int y) { return x + y; } int main() { int arr[10] = {1,2,3,4,5,6,7}; int(*p)[10] = &arr;//p得是数组指针 int* arr2[5]; int* (*p2)[5] = &arr2;//p2则是指针数组 //函数指针 int (*pf)(int, int) = &Add; //函数指针数组 int (* pfarr[4])(int, int); int (* (*p3)[4])(int, int) = &pfarr; //p3是一个指向函数指针数组的指针 return 0; }
注意指向函数指针数组的指针的写法。
(*p3)是指针然后再将类型往外面套就行了。
int ( * [4])(int, int)
= &pfarr;也就是这一部分。
接下来看看应该如何使用呢?
int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int Mul(int x, int y) { return x * y; } int Div(int x, int y) { return x / y; } int main() { int (*parr[4])(int, int) = { Add, Sub, Mul, Div }; int (*(*p)[4])(int, int) = &parr; for (int i = 0; i < 4; i++) { int ret = (*p)[i](4, 2); printf("%d\n", ret); //int ret = p3[0][i](3, 4); //printf("%d\n", ret); } return 0; }
这便是如何使用。
总结😈
我们在这篇博客中详细的介绍了详数组传参指针传参的方法和细节,以及一些不太常用但是你必须要知道的一些指针类型,貌似在操作系统开发上用的比较多。完结。