目录
1、什么是回调函数
函数指针的调用,即是一个通过函数指针调用的函数;
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,就说这是回调函数。
把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称为异步回调。
#include <stdio.h>
// 回调函数类型定义
typedef void (*Callback)(int);
/*
typedef 的作用它是在定义一个新类型(别名),而不是简单的“A 是 B 的别名”
这里定义的类型是 函数指针类型,不是普通的数据类型
(*Callback)(int) 的解析
*Callback:这是一个指针变量(名称是 Callback)
(int):它指向的函数接受一个 int 参数
void:函数的返回类型是 void
整体合起来:Callback 是一个 指向函数的指针类型
*/
// 接收回调函数作为参数的函数
void processNumbers(int arr[], int size, Callback callback) {
for (int i = 0; i < size; i++) {
callback(arr[i]); // 调用回调函数
}
}
// 实际的回调函数实现
void printNumber(int num) {
printf("Number: %d\n", num);
}
void squareNumber(int num) {
printf("Square: %d\n", num * num);
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("Printing numbers:\n");
// 为什么不需要给函数传递参数?,其实 printNumber 这个地方只需要提供一个地址,还没调用
// processNumbers(numbers, size, printNumber());是错误写法
processNumbers(numbers, size, printNumber);
printf("\nPrinting squares:\n");
processNumbers(numbers, size, squareNumber);
return 0;
}
运行结果:
2、回调函数的作用
回调函数的核心作用是
解耦
和
异步响应事件
,尤其在资源受限、实时性要求高的嵌入式环境中非常实用。以下是其具体作用和应用场景:
2.1. 事件驱动编程
嵌入式系统常基于事件驱动(如按键、定时器、传感器数据到达),回调函数允许开发者:
- 将事件处理逻辑分离:主程序无需轮询检查事件,由底层驱动在事件发生时自动调用预设的回调函数。
- 示例:
-
// 注册按键回调 void button_pressed_callback(void) { printf("Button pressed!\n"); } // 驱动层在检测到按键时调用回调 register_button_callback(button_pressed_callback);
2.2. 硬件中断处理
在中断服务例程(ISR)中,回调函数可以:
- 缩短中断处理时间:ISR仅做紧急操作(如清除标志位),后续逻辑通过回调函数在非中断上下文中执行。
- 避免中断嵌套问题:将耗时操作移出ISR。
- 示例:
-
void data_received_callback(uint8_t data) { // 处理接收到的数据(非中断上下文) } // UART中断中调用回调 void USART1_IRQHandler(void) { if (RXNE_flag_set()) { uint8_t data = USART1->DR; data_received_callback(data); // 触发回调 } }
2.3. 异步通信协议
在UART、I2C、SPI等通信中:
- 非阻塞处理数据:发送/接收完成后通过回调通知应用层。
- 示例(伪代码):
-
void i2c_transfer_done_callback(bool success) { if (success) printf("Transfer completed!\n"); } // 异步发送数据 i2c_send_async(data, length, i2c_transfer_done_callback);
2.4. 定时器管理
定时器超时或PWM周期结束时,回调函数用于:
- 执行周期性任务(如数据采集、LED闪烁)。
- 示例(使用硬件定时器):
-
void timer_callback(void) { static int count = 0; count++; if (count >= 10) turn_off_led(); } // 初始化定时器并注册回调 HAL_TIM_RegisterCallback(&htim, TIM_IT_UPDATE, timer_callback);
-
3、回调函数示例
C语言的 qsort 函数,实现了一个冒泡排序算法,它既能从小到大排序,也能从大到小排序,取决与最后一个参数。
#include <stdio.h>
void sort(int a[], int size, int (*p)(int, int)){
int i, j;
for (i = 0; i < size - 1; i++){
for (j = 0; j < size - 1 - i; j++){
if (p(a[j], a[j + 1])) {
int t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
}
int less(int x, int y){
return (x > y) ? 1 : 0;
}
int greater(int x, int y){
return (x < y) ? 1 : 0;
}
int main(){
int a[10] = {1, 0, 4, 7, 2, 9, 5, 3, 8, 6};
sort(a, 10, greater);//大到小排序
for (int i = 0; i < 10; i++){
printf("%d ", a[i]);
}
printf("\n");
sort(a, 10, less);//小到大排序
for (int i = 0; i < 10; i++){
printf("%d ", a[i]);
}
return 0;
}
运行结果: