函数指针与回调
函数指针与回调函数模式
概述
函数指针是指向函数的指针变量,存储的是函数的入口地址。通过函数指针可以在运行时动态选择要调用的函数,这是实现回调函数、策略模式、事件驱动编程和跳转表的基础。回调函数是一种通过函数指针实现的设计模式,允许调用者将自定义行为注入被调用者。
基础概念
函数指针的声明
函数指针的声明语法比较特殊,需要指明返回类型和参数类型:
// 声明一个指向 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;
}