C语言—函数指针与函数指针数组(含回调函数的使用)。

目录:

  1. 什么是函数指针
  2. 函数指针的定义与使用
  3. 函数指针数组的定义与使用
  4. 回调函数的使用

一、什么是函数指针?

函数指针,顾名思义是一种指针。例如数组指针,数组指针变量中存储的是数组的地址(即数组首元素的地址),那么函数指针中存储的是不是函数的地址呢?

接下来我们一起学习下函数指针的定义

函数指针是指向函数的指针变量。在 C 语言中,函数名可以被视为函数在内存中的地址,因此可以用指针来存储和传递函数的地址。

那么函数指针有什么作用呢?我们也一起来了解下:

 函数指针的作用主要有以下几点:

  1. 回调函数:函数指针允许将一个函数作为参数传递给另一个函数,以便在调用函数时执行被传递的函数。这种机制称为回调。通过回调函数,我们可以在一定条件下执行特定操作,增加了代码的灵活性和可扩展性。

  2. 动态调用函数:函数指针可以通过变量来决定调用哪个函数。这使得在运行时根据不同条件选择不同的函数成为可能。这种动态调用函数的能力在某些情况下可以极大地简化代码逻辑。

  3. 函数指针数组:函数指针可以存储在数组中,从而实现创建函数指针的集合。这样可以根据索引或其他条件方便地选择并调用相应的函数,用于执行特定的任务。

 接下来我们来一一探索函数指针的作用。

二、函数指针的定义与使用。

在C语言中,我们可以用以下方式定义函数指针:

函数的返回类型 (函数指针的名称)(函数的参数列表)

例如:下述代码中 int  (*pf)  (int, int);

#include <stdio.h>

int add(int a, int b) 
{
    return a + b;
}

int main() 
{
    int (*pf)(int, int); // 声明一个函数指针变量

    pf = add; // 将函数指针指向 add 函数

    int result = pf(3, 4); // 调用函数指针执行 add 函数

    printf("Result: %d\n", result);

    return 0;
}

    在上述代码中,对于 pf = add 这一步骤我们来进行详细解释:在 C 语言中,函数名可以被视为函数在内存中的地址,因此可以用指针来存储和传递函数的地址。同时,我们也可以将这一步写为 pf = &add ,这两行代码实现的功能是一样的。

    我们定义函数指针变量 pf 后,将 add 函数的地址传给 pf 变量,那么这个时候我们就可以直接调用 pf 来实现 add 函数所实现的功能

    正如上述代码中  int result = pf(3, 4); 等价于 int result = add(3, 4);

 拓展:

我们同样可以使用 typedef 来间接定义一个函数指针变量。

首先我们来一起了解下typedef的功能typedef是C语言中的一个关键字,用于为已有的数据类型定义新的名称,提供了一种方便、简洁的方式来创建数据类型的别名。

例如:

typedef int Integer;//定义 int 的别名为 Integer

typedef int* IntPtr;//定义 int* 的别名为 IntPtr

当我们后续定义整型变量或整型指针时,就可以如下定义:

Integer a = 10;//定义一个整型变量a,赋值为10

IntPtr  b = &a; //定义一个整形指针b,存放a的地址

那么我们如何使用它来定义一个函数指针呢?

typedef int (*pf)(int, int); //声明一个函数指针类型别名

pf pt; //声明一个函数指针变量

pt = add; //将函数指针指向 add 函数

接下来我们用完整代码展示一下:

#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

int main()
{
    typedef int (*pf)(int, int); // 声明一个函数指针类型别名

    pf pt; // 声明函数指针变量

    pt = add; // 将函数指针指向 add 函数

    int result = pt(3, 4); // 通过函数指针调用 add 函数

    printf("Result: %d\n", result);

    return 0;
}

 三、函数指针数组的定义与使用。

1、函数指针数组的定义与使用

在C语言中,可以使用函数指针数组来保存多个函数指针。函数指针数组的定义方式如下:

返回类型 ( * 函数指针数组名称 【数组大小】)(参数类型)。

注意:一个数组中元素的数据类型都是相同的。所以当我们建立函数指针数组时,我们必须保证函数的返回类型和参数类型都是必须是相同的。

以下是一个示例,展示如何定义和使用函数指针数组:

#include <stdio.h>

int add(int a, int b) ;
int subtract(int a, int b) ;
int multiply(int a, int b) ;

int add(int a, int b) 
{
    return a + b;
}

int subtract(int a, int b) 
{
    return a - b;
}

int multiply(int a, int b) 
{
    return a * b;
}

int main() 
{
    int (*pf[3])(int, int); // 声明一个包含3个函数指针的数组

    pf[0] = add; // 将指针指向 add 函数
    pf[1] = subtract; // 将指针指向 subtract 函数
    pf[2] = multiply; // 将指针指向 multiply 函数

    int result1 = pf[0](3, 4); // 调用 add 函数
    int result2 = pf[1](5, 2); // 调用 subtract 函数
    int result3 = pf[2](2, 3); // 调用 multiply 函数

    printf("Result 1: %d\n", result1);
    printf("Result 2: %d\n", result2);
    printf("Result 3: %d\n", result3);

    return 0;
}

2、函数指针数组的应用场景:

例如:当我们需要写一个“计算器”程序来同时满足加、减、乘、除等运算时,相信大部分同学的思路可能是这样的:

#include <stdio.h>

void menu();
int Add(int x, int y);
int Sub(int x, int y);
int Mul(int x, int y);
int Div(int x, int y);

void menu()
{
	printf("****************************\n");
	printf("***  1. add      2. sub  ***\n");
	printf("***  3. mul      4. div  ***\n");
	printf("***  0. exit             ***\n");
	printf("****************************\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 = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("请输入2个操作数:");
			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;
}

可以看出,这样写出来的代码非常繁杂,并且有很多地方是重复的。但当我们运用函数指针数组来储存这些函数地址并通过数组元素调用函数时,代码会更简单吗?我们来看一下使用函数指针数组后的代码:

#include<stdio.h>

void menu();
int Add(int x, int y);
int Sub(int x, int y);
int Mul(int x, int y);
int Div(int x, int y);

void menu()
{
	printf("****************************\n");
	printf("***  1. add      2. sub  ***\n");
	printf("***  3. mul      4. div  ***\n");
	printf("***  0. exit             ***\n");
	printf("****************************\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 = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};//将数组下标为0的元素用空指针填充,使函数调用直接从下标为1的元素开始
		//                           0     1     2   3    4
		if (0 == input)
		{
			printf("退出计算器\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("ret = %d\n", ret);
		}
		else
		{
			printf("选择错误,重新选择!\n");
		}
	} while (input);

	return 0;
}

可以看到,使用函数指针数组后,不会再有冗余的代码部分。代码量会减少,代码看起来也会更加简洁。

四、回调函数的使用。

首先我们来了解下什么是回调函数:

回调函数(callback)就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,于对该事件或条件进行响应。

回调函数的使用方式如下:

  1. 定义一个函数作为回调函数。这个函数定义了回调函数所要执行的具体逻辑。
  2. 在需要的地方,将回调函数作为参数传递给另一个函数(通常是一个高阶函数)。
  3. 当满足特定的条件或事件发生时,调用传递进来的回调函数。

回调函数也可以使代码变得更为简洁。

其实回调函数可以理解成一个“中介”,比如当买房子时,客户向中介提供具体的买房需求,中介再通过客户需求在房源中为客户找到需要的房型。所以回调函数也可以理解为“中介函数”。

我们依然使用上述的模拟“计算器”的初始程序:

#include <stdio.h>

void menu();
int Add(int x, int y);
int Sub(int x, int y);

void menu()
{
	printf("****************************\n");
	printf("***  1. add      2. sub  ***\n");
	printf("***  0. exit             ***\n");
	printf("****************************\n");
}

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret = %d\n", ret);
			break;

		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误, 重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

那么我们如何使用回调函数使其变得更为简洁呢?

通过回调函数的定义我们可以了解到,回调函数接收的参数中包括函数指针,这个函数指针存储的函数地址就是我们需要使用的功能。下面,我们先定义一个回调函数,它的参数中必须包含接收函数地址的函数指针,并且随着传入函数地址的改变,其功能也随之改变。

void callback(int (*pf) (int , int ));

注意:回调函数的返回类型必须与传入函数的返回类型一致。

我们写出完整程序,使回调函数包含上述代码中每个case后的功能:

#include <stdio.h>

void menu();
int Add(int x, int y);
int Sub(int x, int y);
void callback(int(*pf)(int, int));

void menu()
{
	printf("****************************\n");
	printf("***  1. add      2. sub  ***\n");
	printf("***  0. exit             ***\n");
	printf("****************************\n");
}

void callback(int(*pf)(int, int))
{
	int a, b, ret;
	printf("请输入2个操作数:");
	scanf("%d %d", &a, &b);
	ret = pf(a, b);
	printf("ret = %d\n", ret);
}

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			callback(Add);
			break;
		case 2:
			callback(Sub);
			break;

		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误, 重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

我们可以通过下图来了解到回调函数与其他函数的关系:

以上就是本节的所有内容。在之后的博客《qsort函数的使用》中还将为大家展示回调函数的强大功能。 

C语言—qsort(快速排序)函数的使用。_这题怎么做?!?的博客-CSDN博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这题怎么做?!?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值