STM32-函数指针的原理及应用
函数指针与自定义类型在结构体中的应用解析:通过函数指针实现行为抽象与解耦。首先定义函数指针类型(如send_function),描述函数签名;然后将其作为结构体成员(如Snet_AT),实现可替换逻辑;最后赋值具体函数并调用(如UartSend/NetSend)。典型应用包括回调函数、算法动态替换和接口解耦,核心思想是通过函数指针实现灵活的"插件式"设计,使代码更易扩展和维护。
·

函数指针和自定义函数指针类型在结构体中的应用,核心是通过函数指针实现 “行为(发送 / 接收逻辑)的抽象与解耦”。以下分步骤解析函数指针的定义、赋值和调用,帮你掌握用法:
一、核心概念:函数指针的本质
函数指针是一种指向函数的指针变量,它存储的是函数的入口地址。通过函数指针,你可以像调用普通函数一样调用它指向的函数,常用于实现 “回调函数”“动态替换算法 / 逻辑” 等场景。
二、代码拆解:函数指针的完整使用流程
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;
}
四、函数指针的典型应用场景
- 回调函数:
比如硬件中断发生时,调用你注册的回调函数(通过函数指针实现)。 - 算法动态替换:
像上面的例子,根据需求切换 “串口发送” 或 “网络发送”,无需修改结构体逻辑,只需替换函数指针。 - 接口解耦:
让模块(如Snet_AT)不依赖具体的发送 / 接收实现,只依赖函数指针定义的 “接口”,符合开闭原则(对扩展开放,对修改关闭 )。
五、总结
函数指针的核心用法可归纳为 “3 步”:
- 定义函数指针类型:用
typedef简化语法,描述函数的签名(返回值、参数)。 - 用函数指针作为 “行为抽象”:放到结构体 / 变量里,代表 “可替换的逻辑”。
- 赋值并调用:把实际函数的地址赋给指针,通过指针调用函数,实现 “动态行为”。
理解这一点后,你就能灵活用函数指针实现 “回调”“插件化逻辑” 等设计,让代码更灵活、解耦~
更多推荐



所有评论(0)