关于函数的地址
#include <stdio.h>
void test()
{}
int main()
{
printf("%p\n" ,test); //当前编译器给出的地址0x400526
printf("%p\n" ,&test); //当前编译器给出的地址0x400526
return 0;
}
总结:输出的是两个地址,这两个地址是test函数的地址就一个,不存在什么函数首地址这种说法。那么我们可以把地址交给一个函数指针变量存起来。
1、函数指针
什么是函数指针?
-
函数指针:本质上是一个指针,这个指针指向了一个函数的入口地址。
- 如:int (*p)(int ,int); //函数指针的定义
- int (*p)(); //函数指针的另一种定义方式,不过不建议使用。
- int (*p)(int a, int b); //也可以使用这种方式定义函数指针
- 总结:( )里面p先和*结合,说明p是一个指针,指向的是一个函数,指向的函数参数是int类型,函数返回值为int类型。
- 例子:
int test(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
/*普通函数调用*/
//printf("%d\n" ,test(a,b));
/*定义函数指针*/
//int (*pa)(int,int) = test; //写法一:定义函数指针时,参数可以填可以不填
int (*pa)(int a,int b) = test; //写法二:把test函数的地址存放到pa函数指针变量里面。
/*调用函数指针*/
printf("%d\n" ,(*pa)(a,b)); //写法一:调用函数的时候解引用也可以。
printf("%d\n" ,pa(a,b)); //写法二:调用函数的时候,不用解引用也可以。
return 0;
}
#include <stdio.h>
void my_printf(char *str)
{
printf("%s\n" ,str);
}
int main()
{
void (*pc)(char *) = my_printf;
pc( "hello world"); //调用的时候不解引用也可以
return 0;
}
两段有趣的代码:
出自《C陷阱和缺陷》一书
(* (void (*)()) 0) (); //代码一
void (signal(int , void()(int))) (int); //代码二
(* (void (*)()) 0) (); //((void (*) ())0)这段就是把0强制类型转换成:void (*) ()函数指针类型 0就是一个函数的地址。
//(*-找到0这个函数的地址,并调用该函数) (函数参数void)
//总结来说:这段代码就是一次函数调用。
void (*signal(int , void(*)(int))) (int); //signal是一个函数声明,它有两个参数,第一个参数是int类型,第二个参数是void(*)(int)类型
//第二个参数是一个函数指针,函数参数类型是int的,函数返回值是void。
//signal函数的返回类型是void(* )(int),也就是函数指针。函数参数是int类型,返回类型是void
//总结:这是一次函数声明
//代码二简化
typedef unsigned int uint; //正常来说,我们是这样重命名的。
typedef void(*pfun_t)(int); //但现在这个类型的名字要靠近*。给这个类型重新起一个名字叫pfun_t
pfun_t(*signal(int , pfun_t)); //简化后的写法
2、函数指针数组
数组是一个存放相同类型数据的存储空间,我们知道指针数组是这样的。
比如:
- int* arr[10];//数组的十个元素,每个元素是int*。
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int(* parr[10])();
* parr先和[ ]结合,说明parr是数组,数组的内容是十个int(*)()类型的函数指针。
函数指针数组的用途:转移表
练习:
写一个函数指针和函数指针数组
char* my_strcpy(char* dest ,const char* src);
char* my_strcpy1(char* dest ,const char* src);
char* my_strcpy2(char* dest ,const char* src);
char* my_strcpy3(char* dest ,const char* src);
/*1、写一个函数指针pt。能够指向my_strcpy*/
char* (*pt)(char* ,const char*) = my_strcpy; //函数指针的定义以及初始化
/*2、写一个函数指针数组,能够存放4个my_strcpy*/
//函数指针数组的初始化以及定义
char* (*ptarr[4])(char* ,const char*) = {my_strcpy,my_strcpy1,my_strcpy2,my_strcpy3};
例子:计算器(不用函数指针数组实现)
#include <stdio.h>
void menu()
{
printf("----------------menu-----------------\n");
printf("---------1、Add 2、Sub--------\n");
printf("---------3、Mul 4、Div--------\n");
printf("---------------0、Exit---------------\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, y = 0;
do
{
menu(); //打印出菜单
printf("请选择\n");
scanf("%d",&input);
switch (input)
{
case 1:
printf("请输入数字\n");
scanf("%d%d",&x,&y);
printf("%d\n",Add(x ,y));
break;
case 2:
printf("请输入数字\n");
scanf("%d%d",&x,&y);
printf("%d\n",Sub(x ,y));
break;
case 3:
printf("请输入数字\n");
scanf("%d%d",&x,&y);
printf("%d\n",Mul(x ,y));
break;
case 4:
printf("请输入数字\n");
scanf("%d%d",&x,&y);
printf("%d\n",Div(x ,y));
break;
case 0:
printf("退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}while (input); //选择非0,循环会继续。
return 0;
}
结果:
总结:
不用所谓的函数指针数组,这样其实也可以实现。那么如果我们switch里面不仅仅只有加减乘除,还有添加其它功能,那么我们还是要往里面写很多代码?这样case语句就会越来越长,越来越繁杂。
例子:计算器(使用函数指针数组的形式来实现)
#include <stdio.h>
void menu()
{
printf("----------------menu-----------------\n");
printf("---------1、Add 2、Sub--------\n");
printf("---------3、Mul 4、Div--------\n");
printf("---------5、Xor 0、Exit--------\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 Xor(int x, int y) //这里再加一个异或
{
return x ^ y;
}
int main()
{
int input = 0;
int x, y = 0;
do
{
menu(); //打印出菜单
printf("请选择\n");
scanf("%d",&input);
int (*PfArr[])(int ,int) = {0 ,Add ,Sub ,Mul ,Div ,Xor}; //写一个函数指针数组存放四个函数的地址
if (input >= 1 && input <= 5)
{
printf("请输入数字\n");
scanf("%d%d",&x,&y);
printf("%d\n" ,PfArr[input](x ,y)); //通过得到数组对应的下标,从而选择调用哪个函数。
}
else if (input == 0)
{
printf("退出程序\n");
}
else
{
printf("输入错误,请重新输入\n");
}
}while (input); //选择非0,循环会继续。
return 0;
}
输出结果:
总结:
对比上面常规写法,下面使用函数指针数组,我们把几个函数的地址存放到一个数组里面,通过input选择的值作为数组下标,找到这个元素,直接调用这个元素作为地址的这个函数,减少了编写代码时工作量。从代码执行效率上来说,也比case语句高。假如多个函数中均要作如此处理,函数指针数组更能体现出它的优势。
经过改造后的代码,代码量变少,变得更好维护。(下面使用函数指针数组没有使用switch_case语句)