UART问题解析
一、先破误区:结构体指针数组 →绝对不能直接放结构体变量!
先看你的代码:
// 结构体指针数组 struct UART_Device *g_uart_devs[] = {&g_stm32_uart1};1. 先分清 3 个东西(核心!)
代码写法 | 是什么? | 比喻 |
struct UART_Device | 结构体类型(图纸) | 房子设计图 |
g_stm32_uart1 | 结构体 变量 (实体) | 真实的房子(设备) |
&g_stm32_uart1 | 结构体 指针 (地址) | 房子的 门牌号 |
struct UART_Device *[] | 结构体 指针数组 | 一本 通讯录 |
2. 关键结论
- 结构体指针数组:里面的每个格子,只能存「门牌号(指针 / 地址)」
- 不能直接把「房子(结构体变量)」塞进数组里!
struct UART_Device *g_uart_devs[] = {&g_stm32_uart1};翻译成人话:
我创建了一个通讯录(指针数组),里面只记了1 个门牌号:这个门牌号指向g_stm32_uart1这个真实的串口设备。
3. 为什么要这么写?(嵌入式必知)
- 省空间:指针只有 4 字节(32 位单片机),结构体很大,存地址更高效
- 方便操作:拿到门牌号(指针),就能直接找到房子(设备),调用它的功能
- 统一管理:所有设备的门牌号都放一起,方便查找
二、核心:这个数组 +GetUARTDevice函数 →如何互相配合?
我用 **「通讯录 + 查通讯录」** 的比喻,结合你的代码,一步一动讲联动逻辑:
前置准备(两者配合的基础)
1. 你先造了一个「真实设备」(房子)
// 实体结构体变量:实实在在的uart1设备,自带名字、方法、私有数据 static struct UART_Device g_stm32_uart1 = { .name = "stm32_uart1", .init = stm32_uart_init, .send = stm32_uart_send, .recv = stm32_uart_recv, .priv_data = &g_stm32_uart1_data, };2. 你把「设备的门牌号」放进「通讯录数组」
// 指针数组 = 通讯录:只存设备的地址(门牌号) struct UART_Device *g_uart_devs[] = {&g_stm32_uart1};✅ 此时通讯录状态:g_uart_devs[0]= 门牌号 → 指向g_stm32_uart1设备
正式配合:上层调用GetUARTDevice函数
// 指针数组 = 通讯录:只存设备的地址(门牌号) struct UART_Device *g_uart_devs[] = {&g_stm32_uart1};函数开始工作,和数组完美联动:
struct UART_Device *GetUARTDevice(char *name) { int i = 0; // 👇 第一步:遍历通讯录(指针数组),看有几个门牌号 for(i=0; i<(sizeof(g_uart_devs) / sizeof(g_uart_devs[0])); i++) { // 👇 第二步:拿出通讯录里的第i个门牌号 → 找到对应的房子 // 👇 第三步:比对房子的名字 和 你要找的名字 if(0 == strcmp(name, g_uart_devs[i]->name)) // 👇 第四步:名字匹配!把这个门牌号返回给上层 return g_uart_devs[i]; } return NULL; }逐步骤联动拆解(最关键!)
步骤 1:函数找数组 → 「我要查通讯录了」
for循环先计算数组长度:知道通讯录里有1 个门牌号。
步骤 2:数组给函数 → 「给你第一个门牌号」
g_uart_devs[0]把&g_stm32_uart1这个指针交给函数。
步骤 3:函数用指针 → 「通过门牌号找到房子,看它的名字」
g_uart_devs[i]->name→ 通过指针(门牌号),访问设备结构体的name成员。
步骤 4:名字匹配 → 函数把指针返回上层
return g_uart_devs[i];→ 把这个设备的 ** 门牌号(指针)** 还给上层。
最终结果:
上层拿到指针 → 就能调用这个设备的init/send/recv方法!
三、可视化:它们的关系(一眼看懂)
【上层应用】 ↓(调用 GetUARTDevice("stm32_uart1")) 【查找函数】 ↓(遍历) 【指针数组 g_uart_devs】 ↓(存的是:&g_stm32_uart1 指针) 【实体设备 g_stm32_uart1】 (包含:名字、init、send、recv、私有数据)四、扩展:以后加 UART2,它们怎么配合?
你只需要加 2 行代码,函数完全不用改!这就是配合的强大之处:
// 1. 新增uart2实体设备 static struct UART_Device g_stm32_uart2 = {...}; // 2. 把uart2的门牌号加入通讯录数组 struct UART_Device *g_uart_devs[] = {&g_stm32_uart1, &g_stm32_uart2};✅ 查找函数自动遍历 2 个门牌号,依旧能精准找到设备。
五、终极总结(两句话记住)
1. 结构体指针数组
- 只能存结构体的地址(指针 / 门牌号),不能直接存结构体变量
- 作用:统一管理所有设备的地址,相当于设备通讯录
2. 数组 + 查找函数 的配合逻辑
- 数组是花名册(记所有设备的门牌号)
- 函数是查花名册的人
- 函数通过遍历数组 → 拿到设备指针 → 比对名字 → 返回匹配的设备指针
一句话秒懂
数组负责把所有设备的「门牌号」集中存放,函数负责拿着你给的名字,去数组里挨个查门牌号,找到对应设备就把地址还给你!
