【C/C++】一个实例看 回调函数 / 函数指针 应用与原理

前言:笔者在学习数据结构经典问题“四种方法求解最大子列和”时,遇到了一个需求:计时是重复动作,因此想创建一个可以为算法运行时间计时的函数。由于计时的特殊性(被计时的函数要被掐头又掐尾),因此要设计一个能调用函数的函数。这就涉及到 回调函数 / 函数指针 的应用了(初学C/C++时,不了解回调函数提出的意义,现在因为自己的需求明白了)。

附:最大子列和问题的四种解法为:暴力法O(n3)、不做重复加法O(n2)、二分法O(n log2(n))、在线处理法O(n),详细解析请见笔者的笔记算法实例:最大子列和,实现代码于ds_cou_1_maxSum.cpp

需求分析

#include <time.h>
#include <stdio.h>

#define MAXK 1e5
clock_t start, stop;
double duration;

start = clock();
for (int i = 0; i < MAXK; i++)
{
    foo(10, 1.1);
}
stop = clock();
duration = ((double)(stop - start) / CLK_TCK / MAXK);
printf("ticks2 = %f\n", (double)(stop - start));
printf("duration2 = %6.2e\n", duration);

如上图,foo()为被计时函数,start与stop需要分别在foo()调用前后取值。现在要将计时功能封装,则其设计概念如下。

double timeCal(function())
{
	clock_t start, stop;
	double duration;
	start = clock();
	for (int i = 0; i < MAXK; i++)
	{
    	function();	// 这个function需要能传入
	}
	stop = clock();
	duration = ((double)(stop - start) / CLK_TCK / MAXK);
	return duration;
}

检索解决需求的方案(回调函数)

搜索:C中,如何将函数作为参数在另一个参数中调用?

方案:使用回调函数。

“回调函数…这个名词好像在大一下的课上听过”,先不管那么多了…开始学习回调函数的使用,实现我的需求。

回调函数

指针可以指向数值,当然也可以指向函数(函数指针)

理解回调函数,首先要了解函数指针这个概念。

我们知道,数据是存储在计算机内存上的,计算机要使用这个数据时,要找到这个数据在内存上的位置,就是地址。

而指针,就是指向地址的变量。 如下图蓝线所示。

在这里插入图片描述

数据存在内存上,那函数存在哪里呢?

答:函数也存在内存上呗!函数说白了,就是一堆指令,也是一串01组成的数据。

那自然,也要有指针,用来指向函数的地址,就是函数指针。 如下图。

在这里插入图片描述

在c中,我们用星号(*)声明指针,自然,函数指针也是用星号声明的。例子如下。

int a = 1;				// 声明整数变量
						// (为变量a申请一块内容空间,并赋值)
int foo() { return 0; }	// 声明函数

int  *p_int = &a;		// p_int 是指针,值为a的地址
int (*p_func)() = foo;		// p_func 是指针,指向foo()函数

回调函数就是把函数指针放在参数表中的函数

理解了函数指针,就不难理解回调函数了:把函数指针放在参数表中的函数。

则,我们的计时函数可以写成:

double timeCal(int (*MaxSubseqSum)(int *, int), int A[], int N)
{
    clock_t start, stop;
    double duration;

    start = clock();
    for (int i = 0; i < MAXK; i++)
    {
        MaxSubseqSum(A, N);
    }
    stop = clock();
    duration = ((double)(stop - start) / CLK_TCK / MAXK);
    return duration;
}

可以在main中如下调用:

int main()
{
    int a[15] = {-1, 4, -1, -3, 5, 1, -6, 3, 3, 5, -21, 1, -3, 6, -2};
    int N = 15;
    double duration[4];

    duration[0] = timeCal(MaxSubseqSum1, a, N);
    duration[1] = timeCal(MaxSubseqSum2, a, N);
    duration[2] = timeCal(MaxSubseqSum3, a, N);
    duration[3] = timeCal(MaxSubseqSum4, a, N);
    for (int i = 0; i < 4; i++)
        printf("duration_%d = %6.2e \n", i + 1, duration[i]);

    return 0;
    /*
    duration_1 = 1.46e-006
    duration_2 = 2.80e-007
    duration_3 = 2.70e-007
    duration_4 = 3.00e-008
    数据规模小,分而治之的方法与不做重复加法的方法相比,优势不明显。
    */
}

其中,*MaxSubseqSum代表一个函数指针,而MaxSubseqSum()这一系列的函数要使用两个参数(一个整型数组 / 也可以是数组首个元素的地址、一个整型变量),因此我们要在回调函数中传入他们。

在这里插入图片描述

如上图,*MaxSubseqSum就好像一个…固定了形状的零件?根据main()指令,只要是符合【返回值类型为int,传入参数为int[]与int】这标准形状的,*MaxSubseqSum就可以变成它,并去调用,实现其功能。

参考资料

[1] 这个世界根本没有什么面向对象!
[2] C 函数指针与回调函数 | 菜鸟教程
[3] 函数指针与回调函数
[4] C语言:值传递,地址传递和引用传递(example:值交换)

发布了132 篇原创文章 · 获赞 36 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42815609/article/details/103228767