目录
函数指针
函数的地址
函数指针的创建
函数地址的使用
typedef及其必要性
typedef
typedef的必要性
函数指针数组(转移表)
函数指针
函数指针,顾名思义,就是存放函数地址的指针
函数的地址
函数也有地址吗?我们来看一段代码:
#include <stdio.h>
void func()
{
;
}
int main()
{
printf(" func = %p\n", func );
printf("&func = %p\n", &func);
return 0;
}
在一次运行中其结果为:
func = 00007FF60C0211A4
&func = 00007FF60C0211A4
可见,函数是有地址的。而且和数组一样,可以用取地址操作符获取其地址,而且函数名本身也代表了其地址。那我们得到了函数的地址有什么用呢?
我们可以通过地址来调用函数,这部分下面讲。
函数指针的创建
int (*pf) (int, float);
int (*pf) (int a, float b); //一个意思,都行
上方就是创建了一个名叫pf的函数指针,而这个函数的返回值就是前面的int,函数的变量为后面括号里的int与float。其实我们可以发现,定义函数指针的语句与声明函数的语句的区别只有在函数名/函数指针名外面包上一个括号并加一个星号
而这个函数指针变量的变量类型是:
int (*) (int, float)
函数地址的使用
既然得到了函数的地址,我们就可以用地址来调用函数。其实这里写法与直接调用函数类似,或者不如说直接调用函数就可以理解为函数名是一个函数指针:
#include <stdio.h>
int test()
{
return 1;
}
int main()
{
int a = test();
int (*pf)() = &test;
int b = pf();
printf("%d", a);
printf("%d", b);
return 0;
}
这里输出结果a=b=1,我们发现函数指针可以直接用来调用函数。
这具体能给我们的代码带来什么变化呢?下面讲。
typedef及其必要性
typedef
typedef可以用来给类型名起“绰号”,让代码更易读易写:
typedef unsigned long long ull;
这里就是给unsigned long long起了一个叫ull的“绰号”,之后我们就能直接使用ull了,比如直接定义unsigned long long类型的变量:
ull a = 1145141919810;
我们也可以给指针类型和数组类型起“绰号”:
typedef int* pt_i;
typedef int[5] pt_arr;
同样的,也可以给指针数组、数组指针“起绰号”,但是复杂一点。
我们假设指针数组的类型是(int(*)[9]),数组指针的类型是(int*[9])
我们需要像定义这两种类型变量一样,把“别名”放在原本变量名的位置,如:
typedef int (*type1)[9];
typedef int *type2[9];
这里就成功的起了type1和type2两个“绰号”。
那么再试试给函数指针“起绰号”,其过程和数组指针类似
我们假设函数指针的类型是(int (*) (int, float)):
typedef int (*type3) (int, float);
typedef的必要性
我们先看个语句:
(*(void (*)())0)();
这个代码十分晦涩,易读性很差。
那如果写成这样呢:
typedef void(*type_pfun)();
type_pfun pfun;
pfun = (type_pfun)0;
(*pfun)();
我们可以看到,这四条语句就好理解多了:
第一行将一个参数为空返回为空函数指针的类型起绰号为type_pfun
第二行定义了一个type_pfun类型的函数指针pfun
第三行将0强制类型转换为type_pfun,并赋值给pfun
第四行解引用pfun,调用这个无输入无输出的函数
四行合起来就是去调用一个0地址处的无参数无输出的函数
这里就能明显的看出typedef对代码易读性的重要作用了。
再看一个:
void (*signal(int , void(*)(int)))(int);
同样的,可以把代码拆为:
typedef void (*type_pfun)(int);
type_pfun signal(int, type_pfun);
这样,我们明显能看出,其实就是声明了一个叫signal的函数。这个函数的返回类型是一个函数指针,参数也有一个函数指针。而这两个函数指针都指向了有一个int类型参数无返回值的函数。
函数指针数组(转移表)
函数指针数组,也叫转移表,就是存放函数指针的数组。
和前面指针数组的逻辑类似,要让最终定义出来的是数组,就需要让变量名先与方括号结合:
int (*pfunarr[6])();
这就定义了一个叫pfunarr的数组,数组长度6,存放(int(*)())类型的函数指针
其使用方式与正常的数组一致,我们可以利用其实现在不同条件下调用不同的函数
比方说,在用户选择的模式不同时进行不同计算的计算器:
#include <stdio.h>
typedef int (*f_calc)(int, int);
void menu()
{
printf("*******************\n");
printf("***1.ADD***4.DIV***\n");
printf("***2.SUB***5.MOD***\n");
printf("***3.MUL***0.exit**\n");
printf("*******************\n");
}
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 MOD(int a, int b)
{
return a % b;
}
int main()
{
int m = 0, n = 0, mode = 0;
f_calc calc[6] = {NULL, ADD, SUB, MUL, DIV, MOD};
menu();
printf("Type in calc mode:");
scanf("%d", &mode);
printf("Type in the two calc number:");
scanf("%d%d", &m, &n);
printf("The answer is: %d\n", calc[mode](m, n));
return 0;
}