c语言函数指针与回调函数

c语言函数指针与回调函数

什么是函数指针

函数指针的本质是指针,它是一个指针变量,在32位系统中它所占的空间大小为4字节,函数指针的值是指向的函数的地址(在计算机中函数名是对应函数的首地址),指向函数的指针。

函数在编程语言或计算机内存系统中会有一个存放的地址空间,也就是存放运行指令或二进制数据的地址空间,函数所占用的内存是一块连续的地址空间,我们要访问这个函数,首先要找到函数对应的地址,就好比我们要串门去见朋友,我们要知道这个朋友家的地址门牌号是多少,找到了地址也就找到了函数的入口。函数名就是这个函数的首地址或叫做起始地址(也就是朋友家的大门)。函数指针可以理解为一个交通工具,可以将你带到函数的入口处。函数指针的值就指向函数的地址。

我们这里有一段代码

#include <stdio.h>

int main()
{
	printf("hello\n");
	
	return 0;
}

上面的函数会被编译器编译为机器语言保存在计算机系统的内存,形成特定指令,这些指令在内存中的分布可以用下图示意

image-20210205224631948

我们就可以利用函数指针指向这个函数的首地址,去访问这个函数。一个函数就是这些指令组成的,在内存中,一个函数就是一块连续的内存,函数的调用就是跳转到某个函数的入口处,也就是函数的首地址(函数的第一条指令)。

函数指针的用法

我们先来看如何写,先来写下面一段代码

#include <stdio.h>

int function1(int x,int y)
{
	int z;
	z = x + y;
	
	return z;
}

int main()
{
	int a;
	int (*p)(int,int);//初始化函数指针
	
	p = &function1;
	a = (*p)(2,3);
	
	printf("a = %d\r\n",a);	
}

上面的function1函数,是计算求和的功能函数,我们想在主函数中使用它,首先要初始化函数指针,这里注意的是函数返回类型和形参类型要对应,p = &function1;这个我们是将函数的首地址赋给p,这里也可以直接写成p = function1;接下来就是解引用,给我们所调用的函数传入参数,计算求和,这样就可以得到函数的计算结果。这里要注意的是对应的函数类型要一致,否则将会报错,如下图,将int (*p)(int,int);返回类型改为void ( * p)(int,int);那在编译器中也是无法通过的。

image-20210402215619135

回调函数的定义

回调函数是函数指针的一个重要应用场景,回调函数是一个通过函数指针调用的函数,把一个函数的指针作为参数传递到另一个函数的参数列表中,让这个函数的指针被调用它所指的那个函数时,这种就是回调函数。

回调函数首先要定义一个函数指针如void (*p)();void是无返回值类型,( * p)是定义的函数指针,()里面是不带参数的,接下来我们就看看如何使用回调函数

#include <stdio.h>

void call_back_function()
{
    printf("hello world!\r\n");
}

void function1(void (*pfc)())
{
    pfc();
}

int main()
{
    void (*p)() = call_back_function;
    function1(p);
}

通过上面的函数我们可以得到打印结果“hello world!”

image-20210403101534095

我们直接从主函数里面看,首先是初始化函数指针,我们把p初始化为call_back_function的地址,然后调用function1函数,向它传入指针p,实现对call_back_function的回调,这里call_back_function就是回调函数,我们没有直接调用这个函数,二是通过传入函数的地址实现回调函数的功能。上面主函数中的写法也可以改为如下方式

int main()
{
    function1(call_back_function);
}

其结果是一样的,在有些代码中看到的可能是这种写法。

回调函数的应用价值

我们先来看个实际的回调函数应用场景

我们有多个功能函数,需要在不同的条件下分别调用,比如说这里有3个功能函数,需要在不同的标志条件下调用,flag == 0时调用函数0,flag == 1时调用函数1,flag == 2时调用函数2

#include <stdio.h>

void function0()
{
    printf("flag == 0\r\n");
}
void function1()
{
    printf("flag == 1\r\n");
}
void function2()
{
    printf("flag == 2\r\n");
}

int main()
{
    int flag = 0;
    
    if(flag == 0)
    	function0();
    else if(flag == 1)
        function1();
    else if(flag == 2)
        function2();
}

上面是我们采用传统的写法,通过if-else if进行flag的判断,这里如果有更多的函数,10个、100个那么这里的判断势必会变得冗余麻烦,这就可以借助函数指针和回调函数实现这一功能,这个实际应用在我们的嵌入式代码中也经常见到。那么就上面的例子而言我们进行一下修改,这里只针对main函数进行修改,这里的flag可以是传入的或其他条件改变的,这里只做演示。

int main()
{
	void(*pfc[])() = {function0,function1,function2};
	
	(*pfc[1])();
}

上面我们利用了一个函数指针数组(*pfc[]) ,数组里的每一个元素都是指针,我们将数组中的元素赋值function0,function1,function2,这样我们在调用的时候根据数组下标进行判断所需要的函数,在使用上可以简化代码,并且随意增加回调函数,只需要在数组中添加即可。

实际在应用中还有其他的应用场景,比如在回调函数的定义中使用的,定义一个函数用一个指针指向另一个函数,函数的参数是个函数指针,这样我们在使用时仅调用这个带参数的函数,通过调用带函数指针参数的函数,实现对其他功能函数的回调,比如下面的用法

#include <stdio.h>

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

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

void function(int (*pfc)(int,int))
{
	printf("%d\r\n",(pfc(3,2)));
}
int main()
{
	function(add);
	function(sub);
}

上面的函数是带有参数运算的功能,我们在使用时只需要回调相应的功能函数就可以,在代码编写上可以去掉很多冗余代码。

C语言指针笔记总结,输出的这些干货是学习过程的一个总结分享,学习的内容有的是视频教程、有的是博客文章还有的参照了一些相关书籍,学习、总结、分享与大家共同进步。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值