【C语言】函数指针学习笔记

🧩 一、什么是函数指针?

  • 函数指针是一个指向函数的指针变量,可以间接调用函数。
  • C 中函数名本身就是一个函数地址,可以赋值给函数指针。
  • 函数指针支持动态调用、回调机制、跳转表等高级功能。

🧱 二、函数指针的基本语法结构

✅ 函数指针的通用写法

返回类型 (*函数指针名)(参数类型1, 参数类型2, ...);

示例

int (*sumPtr)(int, int);             // 返回 int,参数两个 int
float (*calc)(double, float, int);   // 返回 float,三个参数

函数与指针例子

void sayHello(void *data) {
    printf("Hello!\n");
}

void (*fp)(void *);
fp = sayHello;
fp(NULL);  // 间接调用 sayHello

🧮 三、函数指针语法要点(括号的重要性)

void (*cb)(void *);     // ✅ 正确,变量名是 cb
void (*)(void *) cb;    // ❌ 错误,语法不合法
  • 括号用于明确 *cb 是函数指针而不是函数返回值。

✂️ 四、使用 typedef 简化函数指针声明

写法:

typedef void (*Callback)(void *);
Callback cb = sayHello;
cb(NULL);

✅ 优点:

  • 更清晰
  • 更易维护
  • 重复使用方便

🚫 五、不推荐使用的宏方式(#define)

#define Callback (void (*)(void *))
Callback cb;  // ✅ 语法上可行,但有陷阱

❌ 不推荐的原因:

  • 宏替换没有类型检查
  • 括号嵌套复杂,容易出错
  • 可读性差,不如 typedef

🧠 六、函数指针的实际应用场景

1. 回调函数机制

#include <stdio.h>

void onEvent(void *data) {
    const char *msg = (const char *)data;
    printf("Event triggered: %s\n", msg);
}

void registerCallback(void (*cb)(void *), void *param) {
    printf("Registering callback...\n");
    cb(param);
}

int main() {
    const char *message = "Hello from main!";
    registerCallback(onEvent, (void *)message);
    return 0;
}

🔹 输出:

Registering callback...
Event triggered: Hello from main!

2. 函数表 / 跳转表

typedef void (*Handler)(void *);

void func1(void *p) { printf("Func1\n"); }
void func2(void *p) { printf("Func2\n"); }

Handler table[2] = {func1, func2};
table[0](NULL);  // 调用 func1

🧨 七、函数指针常见语法陷阱

错误写法问题描述
void (*)(void *) cb;❌ 编译器不知道 cb 是变量名
#define Callback void(*)(void *)❌ 宏展开顺序混乱、类型不明确
int* a, b;❌ 只有 a 是指针,b 是普通 int

🏗️ 八、函数返回结构体 + 多参数的函数指针写法

示例函数:

typedef struct {
    int x;
    float y;
} MyStruct;

MyStruct makeData(int a, double b);

函数指针写法:

MyStruct (*fp)(int, double);
typedef MyStruct (*DataMaker)(int, double);

使用示例:

DataMaker funcPtr = makeData;
MyStruct result = funcPtr(10, 3.14);

🎯 九、为什么不是 int*double*

因为你定义的函数是值传递:

MyStruct makeData(int a, double b);

所以函数指针也必须写成:

MyStruct (*fp)(int, double);  // ✅ 正确

❌ 如果写成:

MyStruct (*fp)(int*, double*);  // 错误!参数类型不一致

会导致无法赋值函数指针,编译器会报错。


⚖️ 十、传值 vs 传指针 对比

类型说明示例
int值传递,复制一份makeData(5, 3.14)
int*指针传递,传地址makeData(&a, &b)

🧵 十一、结构体作为参数或返回值

函数指针也可以:

typedef struct {
    int a;
    int b;
} Pair;

typedef Pair (*PairFunc)(Pair, Pair);     // 两个结构体参数,返回结构体
typedef void (*PairHandler)(Pair *);      // 结构体指针参数

🔧 十二、实战建议与最佳实践

场景建议写法
结构体很大使用结构体指针
函数参数类型复杂使用 typedef 简化函数指针
多个函数统一接口调用函数指针数组
接口回调(如状态机/事件系统)用函数指针实现解耦

🧩 十三、快速对照表

函数定义函数指针类型
int add(int a, int b);int (*fp)(int, int);
void show(float x);void (*fp)(float);
MyStruct build(char*, int);MyStruct (*fp)(char*, int);
void update(MyStruct *p);void (*fp)(MyStruct *);

✅ 总结回顾

知识点要点说明
函数指针语法返回类型 (*指针变量)(参数类型1, 参数类型2...)
括号作用必须用括号将 *指针名 包裹起来,区分函数声明和指针声明
参数类型匹配函数指针类型必须与实际函数签名一致
typedef 简化使用 typedef 更优雅地声明复杂函数指针类型
传值 / 传指针区别是否复制实参 or 传递地址,对应类型不同
实用技巧用于回调、注册器、接口抽象、状态机、跳转表等高阶场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值