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;保存回调时也要确保其函数类型正确。

一句话记忆:函数指针保存“调用哪个函数”;注册只保存地址,调用函数指针时才真正执行回调。