前置知识: C

函数指针与回调

00:00
2 min Intermediate 2026/6/14

函数指针与回调函数模式

概述

函数指针是指向函数指针变量,存储的是函数的入口地址。通过函数指针可以在运行时动态选择调用函数,这是实现回调函数策略模式事件驱动编程和的基础。回调函数是一种通过函数指针实现的设计模式允许调用者将自定义行为注入调用者。

基础概念

函数指针的声明

函数指针声明语法比较特殊,需要指明返回类型和参数类型

// 声明一个指向 int(int, int) 类型函数的指针
int (*func_ptr)(int, int);

// 对比:声明一个返回 int* 的函数
int *func(int, int);

函数名就是地址

在C语言中,函数名本身就是函数的入口地址,与数组类似

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

// 以下两种写法等价
int (*p1)(int, int) = add;
int (*p2)(int, int) = &add; // & 是可选的

回调函数的概念

回调函数是作为参数传递给另一个函数函数指针,在特定时机被调用

调用者 -> 调用库函数(传入回调) -> 库函数在适当时机 -> 回调调用者的函数

快速上手

基本函数指针

#include <stdio.h>

int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

int main(void) {
    // 声明函数指针并赋值
    int (*operation)(int, int) = add;

    // 通过函数指针调用函数
    printf("3 + 4 = %d\n", operation(3, 4));       // 7

    // 切换到另一个函数
    operation = subtract;
    printf("10 - 3 = %d\n", operation(10, 3));      // 7

    operation = multiply;
    printf("5 * 6 = %d\n", operation(5, 6));        // 30

    return 0;
}

回调函数

#include <stdio.h>

// 遍历数组,对每个元素调用回调函数
void for_each(int *arr, int n, void (*callback)(int)) {
    for (int i = 0; i < n; i++) {
        callback(arr[i]);
    }
}

// 回调函数:打印元素
void print_element(int x) {
    printf("%d ", x);
}

// 回调函数:累加
int sum = 0;
void accumulate(int x) {
    sum += x;
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};

    // 使用打印回调
    for_each(arr, 5, print_element);
    printf("\n"); // 输出: 1 2 3 4 5

    // 使用累加回调
    sum = 0;
    for_each(arr, 5, accumulate);
    printf("总和: %d\n", sum); // 输出: 15

    return 0;
}

详细用法

使用 typedef 简化函数指针

#include <stdio.h>

// 使用 typedef 定义函数指针类型
typedef int (*BinaryOp)(int, int);
typedef void (*Callback)(int);

int add(int a, int b) { return a + b; }
int max(int a, int b) { return a > b ? a : b; }

int main(void) {
    // 使用 typedef 后,声明更清晰
    BinaryOp op1 = add;
    BinaryOp op2 = max;

    printf("add(3, 5) = %d\n", op1(3, 5));   // 8
    printf("max(3, 5) = %d\n", op2(3, 5));    // 5

    // 函数指针数组
    BinaryOp ops[] = {add, max};
    printf("ops[0](10, 20) = %d\n", ops[0](10, 20)); // 30
    printf("ops[1](10, 20) = %d\n", ops[1](10, 20)); // 20

    return 0;
}

带上下文的回调

#include <stdio.h>

// 回调函数类型:带用户数据指针
typedef void (*CallbackWithContext)(int value, void *user_data);

void for_each_with_context(int *arr, int n,
                           CallbackWithContext callback, void *user_data) {
    for (int i = 0; i < n; i++) {
        callback(arr[i], user_data);
    }
}

// 回调:查找最大值
void find_max(int value, void *user_data) {
    int *max_val = (int *)user_data;
    if (value > *max_val) {
        *max_val = value;
    }
}

// 回调:过滤并计数
void count_positive(int value, void *user_data) {
    int *count = (int *)user_data;
    if (value > 0) {
        (*count)++;
    }
}

int main(void) {
    int arr[] = {-3, 5, -1, 8, 2, -7, 4};

    // 查找最大值
    int max_val = arr[0];
    for_each_with_context(arr, 7, find_max, &max_val);
    printf("最大值: %d\n", max_val); // 8

    // 计数正数
    int positive_count = 0;
    for_each_with_context(arr, 7, count_positive, &positive_count);
    printf("正数个数: %d\n", positive_count); // 4

    return 0;
}

qsort 回调

#include <stdio.h>
#include <stdlib.h>

// 升序比较
int cmp_asc(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

// 降序比较
int cmp_desc(const void *a, const void *b) {
    return *(int *)b - *(int *)a;
}

// 按绝对值比较
int cmp_abs(const void *a, const void *b) {
    int va = abs(*(int *)a);
    int vb = abs(*(int *)b);
    return va - vb;
}

void print_array(int *arr, int n) {
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    printf("\n");
}

int main(void) {
    int arr[] = {5, -3, 8, -1, 2, -7, 4};
    int n = sizeof(arr) / sizeof(arr[0]);

    qsort(arr, n, sizeof(int), cmp_asc);
    printf("升序: "); print_array(arr, n);

    qsort(arr, n, sizeof(int), cmp_desc);
    printf("降序: "); print_array(arr, n);

    qsort(arr, n, sizeof(int), cmp_abs);
    printf("绝对值: "); print_array(arr, n);

    return 0;
}

常见场景

场景一:事件系统

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_HANDLERS 10

typedef enum {
    EVENT_CLICK,
    EVENT_KEY_PRESS,
    EVENT_TIMER
} EventType;

typedef struct {
    EventType type;
    int x, y;
    int key_code;
} Event;

typedef void (*EventHandler)(const Event *);

// 事件处理器注册表
typedef struct {
    EventHandler handlers[MAX_HANDLERS];
    int count;
} EventSystem;

void event_system_init(EventSystem *es) {
    es->count = 0;
}

void event_system_subscribe(EventSystem *es, EventHandler handler) {
    if (es->count < MAX_HANDLERS) {
        es->handlers[es->count++] = handler;
    }
}

void event_system_emit(EventSystem *es, const Event *event) {
    for (int i = 0; i < es->count; i++) {
        es->handlers[i](event);
    }
}

// 具体处理器
void on_click(const Event *e) {
    printf("点击: (%d, %d)\n", e->x, e->y);
}

void on_key(const Event *e) {
    printf("按键: %d\n", e->key_code);
}

void logger(const Event *e) {
    printf("[日志] 事件类型: %d\n", e->type);
}

int main(void) {
    EventSystem es;
    event_system_init(&es);

    event_system_subscribe(&es, on_click);
    event_system_subscribe(&es, on_key);
    event_system_subscribe(&es, logger);

    Event click = {EVENT_CLICK, 100, 200, 0};
    event_system_emit(&es, &click);

    return 0;
}

场景二:策略模式

#include <stdio.h>
#include <stdlib.h>

typedef double (*DiscountStrategy)(double price);

// 打折策略
double no_discount(double price) { return price; }
double ten_percent_off(double price) { return price * 0.9; }
double twenty_percent_off(double price) { return price * 0.8; }
double fixed_50_off(double price) { return price > 50 ? price - 50 : 0; }

// 计算最终价格
double calculate_price(double original_price, DiscountStrategy strategy) {
    return strategy(original_price);
}

int main(void) {
    double price = 200.0;

    printf("原价: %.2f\n", price);
    printf("无折扣: %.2f\n", calculate_price(price, no_discount));
    printf("九折: %.2f\n", calculate_price(price, ten_percent_off));
    printf("八折: %.2f\n", calculate_price(price, twenty_percent_off));
    printf("减50: %.2f\n", calculate_price(price, fixed_50_off));

    return 0;
}

场景三:跳转表

#include <stdio.h>
#include <stdlib.h>

// 计算器操作
double calc_add(double a, double b) { return a + b; }
double calc_sub(double a, double b) { return a - b; }
double calc_mul(double a, double b) { return a * b; }
double calc_div(double a, double b) { return b != 0 ? a / b : 0; }

// 跳转表:用数组替代 switch-case
typedef double (*CalcOp)(double, double);

CalcOp calc_ops[] = {
    calc_add,  // 0: 加
    calc_sub,  // 1: 减
    calc_mul,  // 2: 乘
    calc_div   // 3: 除
};

const char *op_names[] = {"+", "-", "*", "/"};

double calculate(int op, double a, double b) {
    if (op >= 0 && op < 4) {
        return calc_ops[op](a, b);
    }
    printf("无效操作: %d\n", op);
    return 0;
}

int main(void) {
    double a = 10.0, b = 3.0;

    for (int op = 0; op < 4; op++) {
        printf("%.1f %s %.1f = %.2f\n",
               a, op_names[op], b, calculate(op, a, b));
    }

    return 0;
}

注意事项

函数指针类型必须匹配

函数指针返回类型和参数类型必须与目标函数完全一致:

int func(int x) { return x * 2; }

// 正确:类型匹配
int (*p1)(int) = func;

// 错误:参数不匹配
// double (*p2)(int) = func;  // 返回类型不同
// int (*p3)(double) = func;  // 参数类型不同

不要调用空函数指针

int (*func_ptr)(int) = NULL;

// 错误:调用空函数指针是未定义行为
// func_ptr(42);  // 崩溃!

// 正确:调用前检查
if (func_ptr) {
    func_ptr(42);
}

函数指针的比较

函数指针可以与 NULL 比较,也可以比较两个函数指针是否指向同一函数

int (*p1)(int, int) = add;
int (*p2)(int, int) = add;

if (p1 == p2) {
    printf("指向同一函数\n");
}

进阶用法

返回函数指针的函数

#include <stdio.h>
#include <stdlib.h>

typedef int (*Operation)(int, int);

int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

// 根据字符返回对应的操作函数
Operation get_operation(char op) {
    switch (op) {
        case '+': return add;
        case '-': return subtract;
        case '*': return multiply;
        default:  return NULL;
    }
}

int main(void) {
    Operation op = get_operation('+');
    if (op) {
        printf("5 + 3 = %d\n", op(5, 3));
    }
    return 0;
}

闭包模拟(带状态的回调)

#include <stdio.h>
#include <stdlib.h>

// 使用结构体模拟闭包
typedef struct {
    int threshold;
    int count;
    void (*callback)(int value, void *context);
} FilterContext;

void filter_and_call(int *arr, int n, FilterContext *ctx) {
    for (int i = 0; i < n; i++) {
        if (arr[i] > ctx->threshold) {
            ctx->callback(arr[i], ctx);
        }
    }
}

void count_above(int value, void *context) {
    FilterContext *ctx = (FilterContext *)context;
    ctx->count++;
    printf("超过阈值: %d (第%d个)\n", value, ctx->count);
}

int main(void) {
    int arr[] = {10, 25, 5, 30, 15, 40, 8};
    FilterContext ctx = { .threshold = 20, .count = 0, .callback = count_above };

    filter_and_call(arr, 7, &ctx);
    printf("总计: %d 个超过 %d\n", ctx.count, ctx.threshold);

    return 0;
}

插件架构

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_PLUGINS 10

typedef struct {
    const char *name;
    void (*init)(void);
    void (*process)(const char *input, char *output, size_t size);
    void (*cleanup)(void);
} Plugin;

static Plugin plugins[MAX_PLUGINS];
static int plugin_count = 0;

// 注册插件
void register_plugin(const Plugin *p) {
    if (plugin_count < MAX_PLUGINS) {
        plugins[plugin_count++] = *p;
    }
}

// 初始化所有插件
void init_all_plugins(void) {
    for (int i = 0; i < plugin_count; i++) {
        if (plugins[i].init) plugins[i].init();
    }
}

// 处理数据(通过所有插件)
void process_through_plugins(const char *input) {
    char buf1[256], buf2[256];
    strcpy(buf1, input);

    for (int i = 0; i < plugin_count; i++) {
        if (plugins[i].process) {
            plugins[i].process(buf1, buf2, sizeof(buf2));
            strcpy(buf1, buf2);
        }
    }

    printf("最终输出: %s\n", buf1);
}

// 示例插件:转大写
void upper_init(void) { printf("大写插件初始化\n"); }
void upper_process(const char *input, char *output, size_t size) {
    size_t i;
    for (i = 0; input[i] && i < size - 1; i++) {
        output[i] = (input[i] >= 'a' && input[i] <= 'z') ? input[i] - 32 : input[i];
    }
    output[i] = '\0';
}

// 示例插件:添加前缀
void prefix_init(void) { printf("前缀插件初始化\n"); }
void prefix_process(const char *input, char *output, size_t size) {
    snprintf(output, size, "[PREFIX] %s", input);
}

int main(void) {
    // 注册插件
    register_plugin(&(Plugin){
        .name = "大写转换",
        .init = upper_init,
        .process = upper_process,
        .cleanup = NULL
    });

    register_plugin(&(Plugin){
        .name = "前缀添加",
        .init = prefix_init,
        .process = prefix_process,
        .cleanup = NULL
    });

    init_all_plugins();
    process_through_plugins("hello world");

    return 0;
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式