目录

函数指针

函数的地址

函数指针的创建

函数地址的使用

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;
}