> 文档中心 > 指针变强之路,学无坦途,深入理解指针进阶【二】

指针变强之路,学无坦途,深入理解指针进阶【二】

【前言】在上一篇文章中提到了指针进阶的第一部分内容,可能有小伙伴们并没有完全掌握,没有掌握的小伙伴们可以点击链接进入:指针变强之路,学无坦途,深入理解指针进阶【一】_Catzzz666的博客-CSDN博客。指针进阶的内容就分为这两个部分,可能已经有不少小伙伴知道指针初阶的内容,并且掌握了一部分指针的内容,但是对于指针这个硬核知识我们了解的越深越好,了解的越多,对我们以后的写代码的能力帮助也就越大,指针进阶【二】大致分为四部分内容:函数指针、函数指针数组、指向函数指针数组的指针以及回调函数的内容。这部分内容博主希望大家能够深入阅读一下,或许这 对大家的在运用指针层面有很大帮助,当然如果有什么问题的话可以直接私信博主,一起加油!接下来进入正文部分:

1.函数指针

关于函数指针,相信有很多小伙伴是一头雾水,函数指针到底是怎么呢?函数指针是怎么存放的?函数指针有什么作用?

下面我们就围绕这几个问题来理解函数指针:

1.1函数指针的定义方式

关于函数指针,首先看下面一段代码:

void test(){printf("hehe\n");}int main(){printf("%p\n", test);printf("%p\n", &test);return 0;}

 看一下输出结果:

 这里输出的其实就是两个地址,这两个地址是test函数的地址,那么我们如果想要把函数的地址保存起来,该怎么做?

下面我们直接上代码:

void test(){printf("hehe\n");}//下面pfun1和pfun2哪个有能力存放test函数的地址?void (*pfun1)();void *pfun2();

 代码中有两种指针地址的存放形式pfun1和pfun2,那么究竟是pfun1的使用更好,还是pfun2更胜一筹?

首先,能够存储地址,就要求pfun1或者pfun2是一个指针变量,那么哪一个是指针呢?

答案是:

    pfun1可以存放。pfun1先和*结合,说明pfun1是指针变量,指针指向的是一个函数,指向的函数无参数值,返回值为void。

那么pfun2为什么不行?这里注意:()的优先级是高于*的,所以pfun2先和()结合,所以pfun2表示的是一个函数,而不是一个指针,返回类型是void*。

纵有疾风起 传奇永不落幕

1.2两段有趣的代码(自主探究)

阅读两段有趣的代码:

//代码1(*(void (*)())0)();//代码2void (*signal(int , void(*)(int)))(int);

 这两段代码在函数指针中是相对较难分析的,如果有小伙伴对这两段代码有兴趣的话,可以自主研究一下,这里就不做过多解释了,当然有问题可以和博主一起讨论,这两段代码来自《C语言陷阱和缺陷》,这本书还是很值得推荐的,里面的内容对初学者还是非常友好的,值得大家入手。

这里提示一下,可以对代码2进行一下简化:

typedef void(*pfun_t)(int);pfun_t signal(int, pfun_t);

2.函数指针数组

2.1函数指针数组的定义方式

数组是一个存放相同类型元素的集合,在创建数组的同时也开辟了空间。

那我们已经学习了指针数组,比如:

int *arr[10];//数组的每个元素是int*

 那要把函数的地址存到一个数组中,那这个函数就叫函数指针数组,那函数指针数组该如何定义?

int (*parr1[10])();int *parr2[10]();int (*)() parr3[10];

答案是:parr1 

parr1先和[ ]结合,说明parr1是数组,数组的内容是什么呢?

是int(*)()类型的函数指针。

2.2函数数组指针的用途:转移表 

说到函数指针数组,那就要聊聊我们日常所使用的计算机了。

计算机是如何实现的呢?下面有两种实现方式:

2.3计算机的两种实现方式

方式一:

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;do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);switch (input){case 1:printf("输入操作数:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("输入操作数:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("输入操作数:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("输入操作数:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}} while (input);return 0;}

方式二(使用函数指针数组实现):

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 = 1)){printf("输入操作数:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);}elseprintf("输入有误\n");printf("ret = %d\n", ret);}return 0;}

对比以上两种代码,代码一种有很多的代码事冗余的,但是代码二在使用函数指针数组后就解决了这种情况。

也就是说函数指针数组的使用简化了代码。 

3.指向函数指针数组的指针

3.1指向函数指针数组

指向函数指针数组的指针是一个指针,

指针指向一个数组,数组的元素是函数指针;如何定义:

void test(const char* str){    printf("%s\n", str);}int main(){    //函数指针pfun    void (*pfun)(const char*) = test;    //函数指针的数组pfunArr    void (*pfunArr[5])(const char* str);    pfunArr[0] = test;    //指向函数指针数组pfunArr的指针ppfunArr    void (*(*ppfunArr)[5])(const char*) = &pfunArr;    return 0;}

4.回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其他函数时,我们就说这是回调函数。回调函数不是由该函数的实现直接完成调用函数,而是在特定的事件或条件时由另外的一方调用的,用于对该事件或条件进行响应。

4.1举个例子 

void test(){printf("hehe\n");}void print(void (*pa)()){if (1)pa();}int main(){print(test);return 0;}

4.2自主探究

qsort函数的使用,qsort是C语言中的一个库函数,qsort的传参与我们之前见到的一些库函数是不一样的:

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

前三个参数各位小伙伴们自行研究,这里提示一下qsort的第四个参数,这个参数传递的是有个函数指针,而这个函数也需要自己创建来完成,所以这个函数的使用其实很有研究的必要,大家一起加油!

【结语】函数进阶的内容讲到这里就结束了,但是我们对于指针的学习还远远没有结束,想要知道了解更多指针方面的内容,关注博主不迷路,下一篇文章我会整理出指针常见的一些面试题,并且正在整理中——ing,那么我们这里 散会。