C语言--指针进阶2
创始人
2024-05-27 01:58:04
0

目录

  • 前言
  • 函数指针
  • 函数指针数组
  • 指向函数指针数组的指针
  • 回调函数

前言

本篇文章我们将继续学习指针进阶的有关内容

函数指针

我们依然用类比的方法1来理解函数指针这一全新的概念,如图1
图1

我们用一段代码来验证一下:

int Add(int x, int y)
{return x+y;
}int main()
{printf("%p\n", &Add);printf("%p\n", Add);return 0;
}

打印结果如图2
图2
进一步验证了函数指针确实是存放函数的地址。
值得注意的是,函数名和取地址函数名的结果是一样的,这有别于数组名和取地址数组名

那么如果我们想用一个指针变量来存放函数的地址该怎么书写呢?
同样是类比数组指针的写法,如下:

int (*pf)(int,int) = Add;

这里的pf就是函数指针,在书写的时候只用交代类型即可(int char float等),不需要把形参也写进去

如果我们想通过函数指针调用这个函数怎么书写呢?
如下代码:

int Add(int x, int y)
{return x+y;
}int main()
{int (*pf)(int, int) = Add;printf("%d\n", (*pf)(3, 5));printf("%d\n", pf(3, 5));printf("%d\n", Add(3, 5));return 0;
}

打印结果如图3

图3
所以以上三种形式的书写均可实现函数的调用。

来看两段有趣的代码
先来看第一个:

(*(void (*)())0)();

对于这样复杂的代码,我们来逐步地分析:
1,将0强制类型转换为void (*)() 类型的函数指针。
2,这就意味着0地址处放着一个函数,函数没有参数,返回类型是void。
3,调用0地址处对这个函数。

我们再来看第二个:

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

我们同样来逐步分析:(注意这里的signal并没有和结合)
1,上述的代码是一个函数的声明。
2,函数的名字是signal。
3,函数的参数第一个是int,第二个是void(
)(int)类型的函数指针。
4,该函数指针指向的函数参数是int,返回类型是void。

5,signal函数的返回类型也是一个函数指针。
6,该函数指针指向的函数参数是int,返回类型是void。
这样讲可能还是不好理解,我们再对代码进行一下简化:

typedef int* ptr_t;
typedef void(*pf_t)(int);//将void(*)(int)类型重新起个别名pf_t
int main()
{void(* signal(int,void(*)(int)))(int);pf_t signal(int,pf_t);return 0;
}

函数指针数组

同样是类比数组指针,比如整型数组指针就是存放整形指针的数组,那么函数指针数组就是存放函数指针的数组
我们来看下面这段代码:

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 (*pf[4])(int, int) = { Add,Sub,Mul,Div };int i = 0;for (i = 0; i < 4; i++){int ret = pf[i](8, 4);printf("%d\n", ret);}return 0;
}

打印结果如图4
图4
那么函数指针数组有什么作用呢?
我们可以通过函数指针数组来实现一个简单的计算器:

void menu()
{printf("*********************************************\n");printf("**********    1,add     2,sub   *************\n");printf("**********    3,mul     4,div   *************\n");printf("**********    0,exit            *************\n");printf("*********************************************\n");
}
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 input = 0;int x = 0;int y = 0;int ret = 0;int (*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };do{menu();printf("请选择: >");scanf_s("%d", &input);if (input == 0){printf("退出计算器\n");break;}if (input >= 1 && input <= 4){printf("请输入两个操作数:>");scanf_s("%d %d", &x, &y);ret = pfArr[input](x, y);printf("结果为%d\n", ret);}} while (input);return 0;
}

运行效果如图5
图5
这样我们就通过灵活使用函数指针数组,巧妙的简化了代码,防止冗长。

指向函数指针数组的指针

指向函数指针数组的指针的书写方式如下

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}
int main()
{int (*pf)(int, int) = Add;int (*pfArr[4])(int, int) = { Add,Sub };int (*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针变量return 0;
}

我们分步来理解这个式子
1,ppfArr是一个指针变量。
2,该指针变量指向的是一个数组,有四个元素。
3,该数组的每个元素类型是int (
)(int,int),是一个函数指针。

回调函数

我们先来看概念:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
那么回调函数具体怎么使用呢?看下面这段代码

int main()
{int input = 0;int x, y;int ret = 0;scanf_s("%d", &input);switch (input){case 1:printf("请输入两个操作数:");scanf_s("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);case 2://Subcase 3://Mulcase 4://Div}return 0;
}

我们会发现,case等于不同的数时,总会执行重复的语句。我们能不能这样思考:假设我们把这些重复的语句封装成一个函数,然后把不同运算的函数地址转过去调用呢?

我们定义一个Calc函数:

void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf_s("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);}

这样我们就实现了在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应的这样一个效果(即case等于不同的值是执行不同的响应)。

以上就是本章全部内容,下一章我们将运用回调函数的特性来模拟实现库函数–qsort(快速排序)。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...