函数指针自定义函数指针类型在结构体中的应用,核心是通过函数指针实现 “行为(发送 / 接收逻辑)的抽象与解耦”。以下分步骤解析函数指针的定义、赋值和调用,帮你掌握用法:

一、核心概念:函数指针的本质

函数指针是一种指向函数的指针变量,它存储的是函数的入口地址。通过函数指针,你可以像调用普通函数一样调用它指向的函数,常用于实现 “回调函数”“动态替换算法 / 逻辑” 等场景。

二、代码拆解:函数指针的完整使用流程

1. 定义函数指针类型(typedef 简化语法)

typedef int (*send_function)(const char* pATSend, int len);  
typedef int (*recv_function)(char* pATRecv, int len);  

  • typedef int (*send_function)(...)
    • 这是自定义函数指针类型send_function 是一个 “类型名”,代表 “一类函数的指针”
    • 这类函数的特征:
      • 返回值是 int(通常用于返回状态,比如发送是否成功);
      • 入参是 const char* pATSend(要发送的字符串指针)和 int len(字符串长度)。
    • 同理,recv_function 是 “返回 int、入参为 char* 和 int” 的函数指针类型。
2. 用函数指针类型定义 “可插拔的行为”(结构体成员)

typedef struct {
    send_function sendFunction;  // 用函数指针类型定义成员
} Snet_AT;  

  • Snet_AT 是一个结构体,sendFunction 是它的成员,类型是 send_function(函数指针)。
  • 作用:把 “发送函数的逻辑” 抽象成结构体的一个成员,让 Snet_AT 结构体可以 “持有” 不同的发送逻辑(比如串口发送、网络发送,只需替换 sendFunction 指向的函数即可)。
3. 赋值函数指针(绑定具体函数)

假设我们有两个实际的函数,符合 send_function 的签名:

// 示例:串口发送函数(假设逻辑)
int UartSend(const char* data, int len) {
    // 实际串口发送代码:比如调用硬件驱动、返回发送结果
    printf("UartSend: %s (len=%d)\n", data, len);
    return len;  // 假设发送成功,返回长度
}

// 示例:网络发送函数(假设逻辑)
int NetSend(const char* data, int len) {
    printf("NetSend: %s (len=%d)\n", data, len);
    return len;  // 假设发送成功,返回长度
}

赋值函数指针的方式:

int main() {
    Snet_AT snet;

    // 1. 让 sendFunction 指向 UartSend
    snet.sendFunction = UartSend;  
    // 调用:通过函数指针执行 UartSend
    snet.sendFunction("AT+TEST", 8);  

    // 2. 动态替换行为:让 sendFunction 指向 NetSend
    snet.sendFunction = NetSend;  
    // 调用:通过函数指针执行 NetSend
    snet.sendFunction("AT+NET", 7);  

    return 0;
}

  • 关键逻辑
    • snet.sendFunction = UartSend;:把函数名(UartSend)赋值给函数指针,本质是把函数的入口地址存到 sendFunction
    • snet.sendFunction("AT+TEST", 8);:通过函数指针调用函数,效果和直接调用 UartSend("AT+TEST", 8); 完全一样。

三、完整示例:把收发逻辑都补上

为了更完整,补全 recv_function 和结构体的用法:

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

// 1. 定义函数指针类型
typedef int (*send_function)(const char* pATSend, int len);  
typedef int (*recv_function)(char* pATRecv, int len);  

// 2. 定义结构体:持有发送/接收的函数指针
typedef struct {
    send_function sendFunc;  // 发送函数指针
    recv_function recvFunc;  // 接收函数指针
} Snet_AT;  

// 3. 实际函数:串口发送
int UartSend(const char* data, int len) {
    printf("[Uart] Send: %s (len=%d)\n", data, len);
    return len;
}

// 4. 实际函数:模拟接收(把数据拷贝到缓冲区)
int UartRecv(char* buf, int len) {
    const char* resp = "OK";  // 模拟收到的响应
    int copyLen = strlen(resp) < len ? strlen(resp) : len;
    strncpy(buf, resp, copyLen);
    printf("[Uart] Recv: %s (copied len=%d)\n", buf, copyLen);
    return copyLen;
}

int main() {
    // 初始化结构体,绑定串口收发逻辑
    Snet_AT snet = {
        .sendFunc = UartSend,  
        .recvFunc = UartRecv   
    };

    // 调用发送
    snet.sendFunc("AT+CMD", 6);  

    // 调用接收(准备缓冲区)
    char recvBuf[100] = {0};
    snet.recvFunc(recvBuf, sizeof(recvBuf));  

    // 动态替换行为:假设切换到网络发送(模拟函数)
    snet.sendFunc = NetSend;  // 假设 NetSend 已实现
    snet.sendFunc("AT+NET", 7);  

    return 0;
}

// 模拟网络发送函数(动态替换用)
int NetSend(const char* data, int len) {
    printf("[Net] Send: %s (len=%d)\n", data, len);
    return len;
}

四、函数指针的典型应用场景

  1. 回调函数
    比如硬件中断发生时,调用你注册的回调函数(通过函数指针实现)。
  2. 算法动态替换
    像上面的例子,根据需求切换 “串口发送” 或 “网络发送”,无需修改结构体逻辑,只需替换函数指针。
  3. 接口解耦
    让模块(如 Snet_AT)不依赖具体的发送 / 接收实现,只依赖函数指针定义的 “接口”,符合开闭原则(对扩展开放,对修改关闭 )。

五、总结

函数指针的核心用法可归纳为 “3 步”

  1. 定义函数指针类型:用 typedef 简化语法,描述函数的签名(返回值、参数)。
  2. 用函数指针作为 “行为抽象”:放到结构体 / 变量里,代表 “可替换的逻辑”。
  3. 赋值并调用:把实际函数的地址赋给指针,通过指针调用函数,实现 “动态行为”。

理解这一点后,你就能灵活用函数指针实现 “回调”“插件化逻辑” 等设计,让代码更灵活、解耦~

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐