目录
前言
上一篇文章带大家了解了字符指针、数组指针,指针数组等知识点。本文接上文,带大家了解函数指针,函数指针数组的知识。
一、函数指针
1.函数地址
先看下面一段代码:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
由测试结果:
由测试结果可见,函数名和&函数名得到的结果是一样的,实际经进行操作时,这两者的效果也完全相同。这里要和&数组名所区分,数组名是首元素地址,而取地址数组名是整个数组的地址。
2.函数指针的使用
假设有如下函数:
int Add(int x, int y)
{
return x + y;
}
如果要用指针接收函数Add的地址,可以定义一个接收地址的指针变量pAdd,函数指针就写成如下形式:
int (*pAdd)(int x,int y)
1.pAdd为变量名,由于加了括号pAdd优先和*结合,表示指针变量
2.变量指向的函数有两个形参,一个是int x,一个是int y
3.函数的返回类型为int
4.去掉变量名,留下的int (* )(int x,int y)就是变量类型
然后可以给函数指针赋值:
int (*pAdd)(int x, int y) = &Add;
int (*pAdd)(int x, int y) = Add;由上文可知,由于Add和&Add的效果完全相同,两种赋值形式均可行
用函数指针调用函数:
1.int sum=Add(3,5);
2.int sum=(*pAdd)(3,5);
变种调用
3.int sum=pAdd(3,5);
4.int sum=(*****pAdd)(3,5);
由上一段赋值的讲解中我们可知,通过&Add把函数Add的地址赋值给了函数指针变量pAdd,
因为&Add和Add等价,由此可以推断出Add和pAdd同样等价。通过Add可以直接调用函数(表达式1),所以pAdd不用*解引用也可以实现函数的调用,表达式3成立。因此*在调用时只是一个摆设,即使加很多的*,表达式依然能实现调用的功能,表达式4成立。
注:虽说从语法的角度来说四种表达式都可以实现同样的效果,但是根据书写格式的规范和提高带代码的可读性的方面来思考,不推荐3,4这两种写法。
3.有趣的两段代码
接下来看两份出自《C陷阱和缺陷》的代码
// 代码 1( * ( void (*)() ) 0 ) () ;首先不难看出 void (*)() 是一个函数指针类型,他不需要传参,且返回类型是void, () 是强制转换操作符,如此就把0强制转换为 void (*)() 这样的一个函数的地址,再通过 * 解引用来访问这个函数, () 最后的这个括号就是函数调用操作符。// 代码 2void (* signal ( int , void(*)(int) ) )(int) ;首先判断表达式类型,在表达式最前面有返回类型,由此可见不是函数调用,再表达式最后用分号结尾而不是用大括号继续代码,可见不是函数定义,所以,这是一个函数申明的表达式。 signal 为函数名,函数有两个形参第一个形参类型为 int ,第二个形参类型为 void(*)(int), 也就是函数指针。这时,声明就剩下一个函数的返回类型,把 signal ( int , void(*)(int) )去掉,留下的 void (*)(int) 就是返回类型.代码二过于的繁琐可以用重命名操作符进行优化typedef void ( * pfun_t )( int ); (把 void ( * )( int )重新命名为 pfun_t)pfun_t signal ( int , pfun_t );
二、函数指针数组
1.何为函数指针数组
顾名思义,函数指针数组的本质是一个数组,用来存放函数指针变量。
int ( * parr [ 10 ])(int x,int y);这个语句就是典型的函数指针数组,parr为数组名,由于下标引用[ ]的优先级高于解引用操作符*,parr优先和[ ]结合,形成数组。将 parr [10]去掉,留下的就数组中的元素类型, int (* )(int x,int y),是典型的函数指针类型。
2.函数指针数组的作用
函数指针数组一般用在转移表中,可以达到优化代码,让代码变的易于修改。(见如下代码)
最简单的写法实现加,减,乘,除的计算机的代码如下:
#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;
}
虽说简单粗暴,但是以后如果要新添功能的话,需要添加case语句,使得代码很长,看起来不清晰。
用函数指针数组优化后的代码:
#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;
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;
}
此代码日后修改就只需要定义好函数之后,在转移表中添加函数名,再对input范围稍作修改即可。代码长度也缩短了不少