03 信息隐藏:同事改一行,LED全乱了
03 信息隐藏:同事改一行,LED全乱了
C 语言 OOP 系列导航
用 struct + me 把 LED 封装成对象之后,还不能停在这里。
如果结构体成员暴露在外,别人依然可以绕过接口直接改状态。这会带来一个嵌入式里很常见、也很恶心的问题:软件状态变了,硬件状态没变。
先看问题:直接改字段,硬件没跟着变
假设 LED 对象长这样:
1 | typedef struct { |
正常点亮应该调用:
1 | led_on(&red_led); |
但有人图省事,直接写:
1 | red_led.is_on = true; |
这行代码让软件变量变成了”亮”,但并没有调用:
1 | gpio_write(red_led.pin, true); |
于是系统变成这样:
| 状态 | 结果 |
|---|---|
is_on == true |
软件以为灯亮了 |
| GPIO 没有写高 | 硬件实际没亮 |
后续逻辑依赖 is_on |
判断全部偏掉 |
调试时最麻烦的就是这种问题:日志看起来是对的,现场硬件行为却不对。
一句话先懂:信息隐藏不是故意藏代码,而是防止外部绕过接口,让软件状态和硬件状态脱节。
.h 是菜单,.c 是后厨
一个 C 模块通常由头文件和源文件组成:
| 文件 | 类比 | 应该放什么 |
|---|---|---|
.h |
菜单 | 外部能调用的接口 |
.c |
后厨 | 具体实现、辅助函数、内部状态 |
外部只应该通过菜单点菜,不应该冲进后厨自己翻锅。
LED 模块的头文件可以这样设计:
1 |
|
这里虽然为了教学还把 struct 放在头文件里,但约束非常明确:外部通过 led_xxx() 接口操作对象,不直接改成员。更严格的工程写法是不透明结构体,后面项目复杂时可以再升级。
static 函数:C 语言里的 private
如果 GPIO 写操作散落在多个函数里:
1 | int led_on(Led_t *me) |
一旦硬件改成低电平点亮,就要到处改。
更好的做法是集中到一个内部函数:
1 | static void led_update_hardware(Led_t *me) |
然后所有公开接口都调用它:
1 | int led_on(Led_t *me) |
static 修饰函数时,含义是:这个函数只在当前 .c 文件内部可见。外部文件不能调用 led_update_hardware(),只能调用 led_on()、led_off()。
getter:安全读取,不直接访问
外部想知道 LED 状态怎么办?不要直接读字段,而是提供查询接口:
1 | int led_get_state(const Led_t *me, bool *is_on, uint8_t *brightness) |
这里有三个细节:
| 写法 | 作用 |
|---|---|
const Led_t *me |
表示只读对象,不修改状态 |
is_on != NULL |
调用者可以只取自己关心的值 |
| 返回错误码 | 参数非法时明确失败 |
调用者这样使用:
1 | bool is_on; |
反面写法和正确写法
| 场景 | 不推荐 | 推荐 |
|---|---|---|
| 点亮 LED | led.is_on = true |
led_on(&led) |
| 熄灭 LED | led.is_on = false |
led_off(&led) |
| 查询状态 | 直接读多个字段 | led_get_state() |
| 更新硬件 | 到处写 gpio_write() |
集中到 static 函数 |
| 内部辅助 | 暴露在头文件 | 放 .c 并加 static |
C/C++ 对照
| C 语言 | C++ |
|---|---|
.h 中声明的函数 |
public 方法 |
.c 中的 static 函数 |
private 方法 |
led_get_state() |
getter |
| 不直接改成员 | 访问控制 |
| 统一内部 helper | 私有工具函数 |
C 语言没有 private 关键字,但 static 能让函数和文件级变量仅在本文件可见。这就是 C 语言模块化的基础边界。
嵌入式项目意义
嵌入式代码不是只改变量。很多变量背后对应真实硬件。如果外部直接改状态,就可能出现:
| 问题 | 后果 |
|---|---|
| 软件状态和 GPIO 不一致 | 现场行为和日志对不上 |
| 多处直接操作寄存器 | 硬件改版时难维护 |
| 内部 helper 被外部调用 | 模块边界被打穿 |
| 查询和修改混在一起 | 调试时无法判断是谁改了状态 |
信息隐藏的目标不是”防别人”,而是降低所有人犯错的概率。
下次你直接改 led.is_on 之前,问自己一句:硬件会跟着变吗?










