C语言 ——函数指针变量、函数指针数组、回调函数

目录

一、函数指针变量

1、概念

2、函数调用

3、复杂的函数指针变量

分析:

结论:

二、typdef

普通类型重命名:

指针类型重命名:

三、函数指针数组

1、概念

2、转移表—函数指针数组的用途

模拟计算器

四、回调函数

1、概念

2、应用——模拟计算器


一、函数指针变量

1、概念

函数指针是指针,是指向函数的指针, 是存放函数地址的指针!!

对于函数而言,函数也是具有地址的,而函数指针变量就是存储函数地址的。

&函数名 和 函数名 都代表着地址的意思。

2、函数调用

 

如上所示,在使用指针变量进行函数调用的同时,我们还需要考虑传参的参数。

 而 当pf3 拿去后,只留下 int (*)(int,int)时。

我们便知道,int (*)(int,int)是pf3这个函数指针变量的类型。

3、复杂的函数指针变量

void ( *signal(int , void(*)(int) ) ) ( int );

分析:

 

 

结论:

上面一串代码是一个函数声明。

signal 是一个函数名,它的有两个函数,分别是int类型的和返回值是void的参数是int的函数指针类型。

而signal这个函数的类型又是一个返回值是void的,参数是int类型的函数指针类型。

所以又有一种写法,但这种写法编译器是不会通过的:

void ( * ) ( int ) signal(int , void(*)(int) ) ;

 意思便是,返回类型为void(*)(int)的函数signal ,它的两个参数,一个参数的类型是int,另一个参数的类型是void(*)(int)

二、typdef

typedef是用来重命名的,可以将复杂的类型简单化

普通类型重命名:

typdef unsigned int  unint ;

int main()
{

    unsigned int a = 0;
    unint b = 0;
    //a和b的类型是一样的
    return 0;
}

指针类型重命名:

typdef int*  ptr ;

int main()
{

    int * p1;
    ptr p2;
    //p1和p2的类型是一样的
    return 0;
}

但是对于函数指针变量的重命名又有所不同:

如上一个复杂的函数指针变量为点,signal的函数指针变量类型是void(*)(int)

而要进行重新命名的话,与其他命名方式有所不同。

typdef  void(*) (int)  ptr ;  //错误!

typdef  void(*ptr ) (int) ;  //正确!

//意思是想把 函数指针变量类型 void(*) (int)  重新命名为ptr 

ptr p1 ;

void(*p2) (int) ;

//p1和p2都是函数指针变量,且二者的函数指针变量类型都是一样的,都是void(*) (int) 


//而后 void ( *signal(int , void(*)(int) ) ) ( int );
//又可以写为 ptr signal( int , ptr ) ;

相同,对于数组指针类型也如图函数指针类型的重命名方式一致:

typdef int (*)[10]  parr; //错误!

typdef int (* parr)[10] ; //正确!

//int (*)[10] 是数组指针变量的类型  

 parr p1 [10]  =  &arr;

 int (*p2)[10]  =  &arr;

// p1和p2的指针类型一致

三、函数指针数组

1、概念

函数指针数组:是数组,数组中存放的都是函数指针。

 

如图所示,parr[ ]中parr是一个数组名,而 int(*)(int, int)是一个函数指针变量的类型,二者相结合,就是一个函数指针数组。

而改数组中的每一个元素类型都是int(*)(int, int)

add和sub在数组中充当的是元素,也是函数指针,也是地址。

2、转移表—函数指针数组的用途

模拟计算器

加减乘除的函数调用。

主函数部分:

int main()
{
	int input = 0;
	do 
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		printf("请选择");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			printf("输入两个操作数");
			scanf("%d %d",&x,&y);
			ret = add(x, y);
			break;
		case 2:
			printf("输入两个操作数");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			break;
		case 3:
			printf("输入两个操作数");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			break;
		case 4:
			printf("输入两个操作数");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			break;
		case 0:
			printf("退出\n");
			break;
		defalut:
			printf("选择错误\n");
			break;

		}

	} while (input);
	return 0;
}

使用了函数指针数组后:

int main()
{
	int x = 0;
	int y = 0;
	int ret = 0;
	int input = 0;
	int(*parr[])(int, int) = { 0,add,sub,mul,div };//这一步操作就是转移表
	printf("请选择");
	scanf("%d", &input);//此刻的input是下标
	do
	{
		if (input >= 1 && input <= 4)
		{
			printf("输入两个操作数");
			scanf("%d %d", &x, &y);
			ret = parr[input](x, y);//和函数指针的有些相似 函数指针:ret = pf(x,y)
			printf("%d", ret);
		}
		else if (input == 0)
		{
			printf("退出\n");
			break;
		}
		else
		{
			printf("输入错误请重新选择\n");
		}

	} while (input);
	
	return 0;
}

转移表的局限性:调用函数中的参数的数据类型必须保持一致!

就列如:如果add的参数类型是int和int ,那么和add在同一个函数指针数组中的sub的调用参数类型也必须是int和int

四、回调函数

1、概念

回调函数就是一个通过函数指针调用的函数。

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

2、应用——模拟计算器

当我们需要使用add函数时,我们先调用了add函数的地址交予了cacl然后cacl的指针通过add的地址抵达了add函数并且返回了add中得到的数值。 

int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		printf("请选择");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(add);//将add函数的地址传给calc函数,再有calc通过地址(指针)调用函数add
			break;
		case 2:
			calc(sub);//将sub函数的地址传给calc函数,再有calc通过地址(指针)调用函数sub
			break;
		case 3:
			calc(mul);//将mul函数的地址传给calc函数,再有calc通过地址(指针)调用函数mul
			break;
		case 4:
			calc(div);//将div函数的地址传给calc函数,再有calc通过地址(指针)调用函数div
			break;
		case 0:
			printf("退出\n");
			break;
		defalut:
			printf("选择错误\n");
			break;

		}

	} while (input);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值