09 函数指针与回调函数
1. 函数指针:保存函数地址
void hello(void) { /* ... */ }
void (*fp)(void) = hello;
fp(); // 调用 hello()
void (*fp)(void):fp指向无参数、无返回值的函数。fp()和(*fp)()都可调用该函数。- 括号不能省:
void *fp(void)是“返回void *的函数声明”,不是函数指针变量。
2. 回调:把函数交给别人调用
#include <stdio.h>
void on_event(int status)
{
printf("status=%d\n", status);
}
void trigger(void (*cb)(int))
{
cb(1); // 等价于 on_event(1),前提是 cb 指向它
}
int main(void)
{
trigger(on_event); // 输出 status=1
return 0;
}
void (*cb)(int):指向接收一个int、返回void的函数。trigger(on_event)传递函数地址;cb(1)才执行回调,并传入参数1。- 函数指针的返回值、参数列表要与目标函数的类型兼容。
3. 注册 ≠ 执行
typedef void (*event_cb_t)(int);
static event_cb_t saved_cb = NULL;
void register_cb(event_cb_t cb)
{
saved_cb = cb; // 只保存地址,不执行
}
void notify_event(int event)
{
if (saved_cb != NULL)
saved_cb(event); // 事件到来时才执行
}
执行顺序: register_cb(on_event) → 保存地址 → notify_event(1) → on_event(1)。
4. 蓝牙/TWS 开发时的注意点
- BLE 连接状态、收包通知等事件常通过回调交给应用层处理。
- 回调不等于异步:可以立即调用,也可以稍后由事件触发。
- 回调运行在哪个线程、任务或中断上下文,取决于 SDK;不要自行假设。
- 调用保存的函数指针之前要检查是否为
NULL;保存回调时也要确保其函数类型正确。
一句话记忆:函数指针保存“调用哪个函数”;注册只保存地址,调用函数指针时才真正执行回调。