前言
(1)函数指针,顾名思义,就说函数的指针。
(2)我们学习Linux的时候,经常能够看到使用一个结构体,结构体中很多个成员,我们给这些成员赋于函数名。这样非常方便我们管理一个设备文件。
(3)本文将会详细介绍函数指针。
函数指针介绍
函数指针原型
(1)函数指针的原型是不固定的,他根据你的原函数类型所决定。什么意思呢?看代码(2)根据代码,我们发现其实这个函数指针的定义,和所指向的函数是一样的(也就是说,函数指针p与原函数print是一样的,函数指针a与原函数add是一样的)。最终使用的时候,使用方式和原函数也一样。
(3)这个函数指针我们可以理解为,就是将函数名改了一下,原来是print现在是p,原来是add,现在是a。只不过在定义的时候,需要将print改成(*p),add改成(*a)。
(4)这个时候,肯定会有人问为什么这么做呢,直接使用原函数名不香吗?后续会讲
void print(void) { printf("test\n"); } int add(int a, int b) { return a + b; } int main() { //我们对比这两个函数指针,发现他们的是根据所指向的函数是一样的 //我们可以理解为,就是讲函数名改了一下,原来是print现在是p,原来是add,现在是a。 void(*p)(void) = print; int(*a)(int, int) = add; p(); printf("%d\n",a(1, 2)); return 0; }
函数指针使用的时候不需要*
我们看到上面的代码,发现函数指针在定义的时候,需要写成(*p),但是在使用的时候,直接使用p了。我们可以加上*吗?可以的,不过加上之后没用,可以忽略
//下面的结果是一样的 p(); (*p)(); printf("%d\n", a(1, 2)); printf("%d\n", (*a)(1, 2));
函数指针加深理解
题目一
(1)我们来看下面这个是什么意思。首先,我们从左到右理解,首先是第一个大括号,不用管。之后我们看到*里面是一个(),因为()优先级最高,所以先括号里面的内容。看到括号里面的内容,有没有想到什么?对的,函数指针!这就是一个函数指针。
(2)现在我们可以将下面这个改成(*(函数指针)0)()了。这个时候,我们需要思考了,这个是什么玩意了。我们再次回到上面的函数指针不需要*的内容去。发现什么了吗, p()==(*p)(),而p又是一个函数指针。
(3)那么现在我们是不是也可以这么理解,我们是在将一个地址为0的函数指针进行调用呢?而(函数指针)0就是将0强制类型转换为一个函数指针。
( *(void (*)())0 )();
题目二
(1)看了上面这个代码,现在这个是不是非常好理解了。
(2)先看将他理解为一个void(*signal)(int),xxx我们先不管,从大体上来看,他就是一个函数指针。而signal的内容,也能够一眼看出来,也是一个函数指针。他的第二个参数也是一个函数指针。
void (*signal(int , void(*)(int)))(int);
(3)这个函数如何简化呢?typedef就是很好的重命名方式。
//错误写法 typedef void(*)(void) p; //正确写法 typedef void(*p)(void); //将void(*)(void)函数指针类型冲命名为p //最终简化结果 void (*signal(int , void(*)(int)))(int) == \ p signal(int, p);
函数指针实操
实操一
我们下面模仿一个计算机的功能。输入对应的数字执行相应功能。我们对比不使用函数指针和使用函数指针,是不是发现使用函数指针之后,代码可读性和简洁程度高了很多。
#include <stdio.h> 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 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 <= 4 && input >= 1)) { printf("输入操作数:"); scanf("%d %d", &x, &y); ret = (*p[input])(x, y); } else printf("输入有误\n"); printf("ret = %d\n", ret); } return 0; }
实操二
我们在编写Linux驱动程序的时候,总是能够看到file_operations结构体,其中每个变量都说一个函数指针。我们只需要调用这个结构体中的变量,就可以直接操作到对应的驱动程序。这样在庞大的Linux系统中,会非常方便。
static const struct file_operations hello_drv = { .owner = THIS_MODULE, .read = hello_read, //hello_read为函数指针 .write = hello_write, //hello_write为函数指针 .open = hello_open, //hello_open为函数指针 .release = hello_release //hello_release为函数指针 };