函数指针学习

一、函数指针的核心概念

1. 什么是函数指针?

函数指针本质是一个指向函数入口地址的指针变量,可以通过它间接调用函数。
类似于数据指针保存的是变量的地址,函数指针保存的是函数的地址。

2. 为什么要用函数指针?

  • 动态调用:在运行时决定调用哪个函数
  • 实现回调(Callback):典型应用如事件处理、排序算法
  • 解耦合:将函数调用与具体实现分离

二、函数指针基础语法

1. 函数指针的声明

基本格式:

<C>

返回类型 (*指针变量名)(参数类型列表);

示例1:指向无参数函数的指针

<C>

void (*func_ptr)(void); // 声明一个指向 void func() 的指针

示例2:带参数的指针

<C>

int (*compare)(int a, int b); // 指向 int compare(int, int) 的指针

2. 给函数指针赋值

<C>

// 假设已有函数定义int add(int a, int b) { return a + b; }int sub(int a, int b) { return a - b; }// 将add函数的地址赋给指针int (*operation)(int, int) = add;

3. 通过指针调用函数

<C>

int result = operation(3, 5); // 等价于调用 add(3,5)

三、4种典型应用场景

场景1:函数指针作为参数(回调函数)

// 定义回调类型
typedef void (*Callback)(int status); 

void download_file(const char *url, Callback callback) {
    // 模拟下载完成
    printf("Downloading %s...\n", url);
    callback(100); // 调用回调函数
}

// 实际回调函数
void on_download_finish(int progress) {
    printf("Progress: %d%%\n", progress);
}

int main() {
    download_file("test.zip", on_download_finish);
    return 0;
}
场景2:在结构体中封装函数指针(模拟面向对象)

typedef struct {
    void (*start)(void);
    void (*stop)(void);
} DeviceDriver;

void led_start() { printf("LED ON\n"); }
void led_stop()  { printf("LED OFF\n"); }

int main() {
    DeviceDriver led = { led_start, led_stop };
    led.start(); // 输出 "LED ON"
    return 0;
}
 

 场景3:实现策略模式

typedef int (*SortFunc)(int*, int); // 定义排序策略类型

// 两种不同的排序策略
int bubble_sort(int *arr, int len) { /* ... */ } 
int quick_sort(int *arr, int len)  { /* ... */ }

void sort_array(int *arr, int len, SortFunc strategy) {
    strategy(arr, len); // 动态调用不同算法
}

int main() {
    int data[5] = {3,1,4,5,2};
    sort_array(data, 5, bubble_sort); // 使用冒泡排序
    sort_array(data, 5, quick_sort);  // 使用快速排序
    return 0;
}
场景4:函数指针数组(状态机/菜单系统)

void menu_login() { printf("登录\n"); }
void menu_exit()  { printf("退出\n"); }
void menu_help()  { printf("帮助\n"); }

// 定义函数指针数组
void (*menu_items[])(void) = { menu_login, menu_exit, menu_help };

int main() {
    int choice = 0;
    while (1) {
        printf("输入选项 (0-2): ");
        scanf("%d", &choice);
        if (choice >= 0 && choice <= 2) {
            menu_items[choice](); // 通过索引调用函数
        }
    }
    return 0;
}

四、深入理解:函数指针的内存模型

1. 验证函数地址的可访问性

printf("add函数地址: %p\n", (void*)add); // 输出类似 0x401550
printf("指针保存的地址: %p\n", (void*)operation); // 应与上方一致

2. 对比函数指针与普通指针

  • 数据指针:指向数据的内存区域(可读写)
  • 函数指针:指向代码段(通常只读)

五、常见问题与防御性编程

1. 如何避免空指针调用?        

if (operation != NULL) {
    operation(3, 5);
} else {
    printf("Error: 函数指针未初始化!\n");
}

2. 类型不匹配的危险性

错误示例:

float (*wrong_ptr)(int) = (float (*)(int))add; // 强制转换危险!
wrong_ptr(10); // 可能引发段错误

六、实战项目建议

项目1:实现一个计算器

typedef double (*MathFunc)(double, double);

double add(double a, double b) { return a + b; }
double mul(double a, double b) { return a * b; }

MathFunc operations[] = {add, mul}; // 扩展更多运算...

int main() {
    double result = operations[0](2.5, 3.5); // 执行加法
    printf("Result: %.2f\n", result);
    return 0;
}
项目2:实现异步任务调度器

typedef struct {
    void (*task)(void*); // 任务函数指针
    void *arg;           // 参数指针
} AsyncTask;

void execute_task(AsyncTask *t) {
    if (t && t->task) {
        t->task(t->arg); // 执行任务并传递参数
    }
}

// 示例任务:打印字符串
void print_task(void *arg) {
    char *str = (char*)arg;
    printf("Task Output: %s\n", str);
}

int main() {
    AsyncTask task = { print_task, "Hello World!" };
    execute_task(&task);
    return 0;
}

七、重点注意事项

下一步学习建议:尝试实现一个基于函数指针的插件系统,实现动态加载不同功能模块。

  1. 严格匹配类型

    • 返回类型和参数列表必须与指针声明完全一致
  2. 使用typedef简化复杂声明

  3.   typedef int (*Comparator)(const void*, const void*); 
    Comparator cmp = strcmp; // 用于qsort的比较函数

  4. 嵌入式系统中的特殊应用
    在STM32中常用于中断向量表:
  5. void (* const g_pfnVectors[])(void) = {
        (void (*)(void))((unsigned long)_estack), // 栈顶地址
        Reset_Handler,                            // 复位处理函数
        NMI_Handler,                              // 其他中断处理程序...
    };
  6. C++中的扩展
    C++中可以使用std::function和lambda表达式替代函数指针,但在嵌入式C中仍需掌握传统方法。
  7. 参考资料
  8. 书籍推荐:《C和指针》第13章
  9. GNU C手册:Function Pointers
  10. 在线练习:Godbolt编译器资源管理器(观察汇编代码)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值