当前位置: 首页 > news >正文

C语言回调函数,提升C技巧必备

第一时间看干货文章

1

一、函数指针

在讲回调函数之前,我们需要了解函数指针。

我们都知道,C语言的灵魂是指针,我们经常使用整型指针,字符串指针,结构体指针等。

代码语言:javascript

AI代码解释

int *p1; char *p2; STRUCT *p3; // STRUCT为我们定义的结构体

但是好像我们一般很少使用函数指针,我们一般使用函数都是直接使用函数调用。

下面我们来了解一下函数指针的概念和使用方法。

1. 概念

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

函数指针可以像一般函数一样,用于调用函数、传递参数。

函数指针的定义方式为:

函数返回值类型 (* 指针变量名) (函数参数列表);

“函数返回值类型”表示该指针变量可以指向具有什么返回值类型的函数;“函数参数列表”表示该指针变量可以指向具有什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可。

我们看到,函数指针的定义就是将“函数声明”中的“函数名”改成“(指针变量名)”。但是这里需要注意的是:“(指针变量名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个函数声明了,即声明了一个返回值类型为指针型的函数。

那么怎么判断一个指针变量是指向变量的指针变量还是指向函数的指针变量呢?首先看变量名前面有没有“”,如果有“”说明是指针变量;其次看变量名的后面有没有带有形参类型的圆括号,如果有就是指向函数的指针变量,即函数指针,如果没有就是指向变量的指针变量。

最后需要注意的是,指向函数的指针变量没有 ++ 和 – 运算。

一般为了方便使用,我们会选择:

typedef 函数返回值类型 (* 指针变量名) (函数参数列表);

比如:

代码语言:javascript

AI代码解释

typedef int (*Fun1)(int); //声明也可写成int (*Fun1)(int x),但习惯上一般不这样。 typedef int (*Fun2)(int, int); //参数为两个整型,返回值为整型 typedef void (*Fun3)(void); //无参数和返回值 typedef void* (*Fun4)(void*); //参数和返回值都为void*指针

2. 如何用函数指针调用函数

给大家举一个例子:

代码语言:javascript

AI代码解释

int Func(int x); /*声明一个函数*/ int (*p) (int x); /*定义一个函数指针*/ p = Func; /*将Func函数的首地址赋给指针变量p*/ p = &Func; /*将Func函数的首地址赋给指针变量p*/

赋值时函数 Func 不带括号,也不带参数。由于函数名 Func 代表函数的首地址,因此经过赋值以后,指针变量 p 就指向函数 Func() 代码的首地址了。

下面来写一个程序,看了这个程序你们就明白函数指针怎么使用了:

代码语言:javascript

AI代码解释

#include <stdio.h> int Max(int, int); //函数声明 int main(void) { int(*p)(int, int); //定义一个函数指针 int a, b, c; p = Max; //把函数Max赋给指针变量p, 使p指向Max函数 printf("please enter a and b:"); scanf("%d%d", &a, &b); c = (*p)(a, b); //通过函数指针调用Max函数 printf("a = %d\nb = %d\nmax = %d\n", a, b, c); return 0; } int Max(int x, int y) //定义Max函数 { int z; if (x > y) { z = x; } else { z = y; } return z; }

特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。

代码语言:javascript

AI代码解释

p = Max可以改成 p = &Max c = (*p)(a, b) 可以改成 c = p(a, b)

3. 函数指针作为某个函数的参数

既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。示例:

代码语言:javascript

AI代码解释

#include <stdio.h> #include <stdlib.h> typedef void(*FunType)(int); //前加一个typedef关键字,这样就定义一个名为FunType函数指针类型,而不是一个FunType变量。 //形式同 typedef int* PINT; void myFun(int x); void hisFun(int x); void herFun(int x); void callFun(FunType fp,int x); int main() { callFun(myFun,100);//传入函数指针常量,作为回调函数 callFun(hisFun,200); callFun(herFun,300); return 0; } void callFun(FunType fp,int x) { fp(x);//通过fp的指针执行传递进来的函数,注意fp所指的函数有一个参数 } void myFun(int x) { printf("myFun: %d\n",x); } void hisFun(int x) { printf("hisFun: %d\n",x); } void herFun(int x) { printf("herFun: %d\n",x); }

输出:

4. 函数指针作为函数返回类型

有了上面的基础,要写出返回类型为函数指针的函数应该不难了,下面这个例子就是返回类型为函数指针的函数:

代码语言:javascript

AI代码解释

void (* func5(int, int, float ))(int, int) { ... }

在这里,func5(int, int, float)为参数,其返回类型为void (\*)(int, int)。在C语言中,变量或者函数的声明也是一个大学问,想要了解更多关于声明的话题,可以参考我之前的文章 - C专家编程》读书笔记(1-3章)。这本书的第三章花了整整一章的内容来讲解如何读懂C语言的声明。

5. 函数指针数组

在开始讲解回调函数前,最后介绍一下函数指针数组。既然函数指针也是指针,那我们就可以用数组来存放函数指针。下面我们看一个函数指针数组的例子:

代码语言:javascript

AI代码解释

/* 方法1 */ void (*func_array_1[5])(int, int, float); /* 方法2 */ typedef void (*p_func_array)(int, int, float); p_func_array func_array_2[5];

上面两种方法都可以用来定义函数指针数组,它们定义了一个元素个数为5,类型是 *void (\*)(int, int, float)* 的函数指针数组。

6. 函数指针总结

  1. 函数指针常量 :Max;函数指针变量:p;
  2. 数名调用如果都得如(*myFun)(10)这样,那书写与读起来都是不方便和不习惯的。所以C语言的设计者们才会设计成又可允许myFun(10)这种形式地调用(这样方便多了,并与数学中的函数形式一样)。
  3. 在函数指针变量也可以存入一个数组内。数组的声明方法:int (*fArray[10]) ( int );

二、回调函数

1. 什么是回调函数

我们先来看看百度百科是如何定义回调函数的:

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

这段话比较长,也比较绕口。下面我通过一幅图来说明什么是回调:

假设我们要使用一个排序函数来对数组进行排序,那么在主程序(Main program)中,我们先通过库,选择一个库排序函数(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,我们也可能需要对特殊的对象进行排序,比如特定的结构体等。库函数会根据我们的需要选择一种排序算法,然后调用实现该算法的函数来完成排序工作。这个被调用的排序函数就是回调函数(Callback function)。

结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。

我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。

如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调。

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

http://www.jsqmd.com/news/461956/

相关文章:

  • 5 块钱的无线调试器就这么做好了?
  • 别再混淆网关类型!Camunda中Inclusive/Exclusive/Parallel网关的5个核心区别与选型指南
  • PP-DocLayoutV3真实作品:学位论文首页→doc_title+author+institution+abstract全要素提取
  • AI写的期刊论文靠谱吗?2025年12款写论文的AI推荐,参考文献真实可靠!
  • DAMOYOLO-S对比YOLOv11:速度与精度实测效果分析
  • HC-SR04超声波测距模块在智能小车避障系统中的应用
  • 机器人演示效果好吗?现场同步核对
  • 圣女司幼幽-造相Z-Turbo在儿童美育中的应用:安全过滤后的国风神话角色创意绘画工具
  • STM32 HAL库实现RTC闹钟动态间隔触发(代码详解+优化思路)
  • SpringBoot+Vue 大学生平时成绩量化管理系统平台完整项目源码+SQL脚本+接口文档【Java Web毕设】
  • 基于springboot琼瑶品鉴平台的设计与实现
  • 照着用就行:10个降AI率网站深度测评与推荐
  • 不用写代码!用开源项目MoneyPrinterTurbo打造你的AI短视频工厂(支持GPT-4和Moonshot)
  • Cogito-3B一键量化方案:Ollama快速部署,无需代码5分钟上手
  • 【技术突破】FPGA加速CNN:实时推理的并行计算架构与落地实践
  • Qwen3-Reranker-8B企业级部署:Java微服务集成全指南
  • 零基础小白必看:AI净界RMBG-1.4部署与使用全攻略
  • c语言之函数篇
  • AgentCPM深度研报助手与YOLOv11结合:从研报图表中提取结构化数据
  • 2026年湖北工商注册与资质办理靠谱服务商深度解析 - 2026年企业推荐榜
  • 智能检索升级:用OFA模型搭建图文匹配系统,提升搜索准确率
  • bge-large-zh-v1.5部署体验:简单几步,让中文文本拥有向量表示
  • 【赵渝强老师】使用TiSpark在Spark中访问TiDB
  • CasRel实战教程:使用test.py定制化输入,支持长文本分段SPO抽取
  • 逻辑派FPGA实战:基于RGB接口实现1080P高清HDMI输出的完整方案(来自JerryTech贡献)
  • 从理论到实践:深入解析SBERT架构与sentence-transformers库的核心应用
  • 零基础上手Qwen-Image-2512-SDNQ:浏览器输入文字,一键生成惊艳图片
  • 2026年靠谱的钛钢复合板厂家推荐:耐热钢复合板/爆炸金属复合板厂家推荐及采购参考 - 行业平台推荐
  • 《构建之法》阅读笔记(三)
  • Ubuntu22.04 + Windows11 双系统安装记录及启动界面美化(双NVMe硬盘方案)