13 终章:从自动注册到Linux内核OOP全貌
13 终章:从自动注册到Linux内核OOP全貌 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 ⇐ 当前位置 到这里,C 语言层面的对象、继承、函数指针、ops 和多态都已经就位。最后一篇把视角拉到大型工程 — 你会发现 Linux 内核里的很多设计,也是这套思路,只是规模更大、机制更完整。 先看问题:每加一个模块,都要手动调 init普通项目里,初始化可能集中写在 main(): 1234567891011int main(void){ gpio_driver_init(); pwm_driver_init(); i2c_driver_init(); led_driver_init(); ...
12 完整框架:换硬件不改应用
12 完整框架:换硬件不改应用 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 ⇐ 当前位置 从自动注册到Linux内核OOP全貌 前面讲的封装、继承、函数指针、ops、多态 — 单看都像局部技巧。真正放进项目里,它们会拼成一个目标:应用层不直接依赖具体硬件。 如果应用层直接写底层 API: 123456void app_run(void){ gpio_write(5, true); delay_ms(100); gpio_write(5, false);} LED 从 GPIO 换成 PWM,应用层就要改;再换成 I2C 灯带,又要改。这暴露了一个事实:业务代码被硬件绑住了。 框架分层一个可维护的 LED...
11 接口设计:虚函数不实现会怎样
11 接口设计:虚函数不实现会怎样 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 ⇐ 当前位置 换硬件不改应用 从自动注册到Linux内核OOP全貌 有了 ops 表,就有了 C 语言版虚函数。但如果某个函数指针是 NULL,会怎样?答案很直接:不检查就调用,程序直接崩。 ops 槽位为空假设有人偷懒把 deinit 写成 NULL: 123456static const LedOps_t pwm_led_ops = { .on = pwm_led_on, .off = pwm_led_off, .set_brightness = pwm_led_set_brightness, .deinit = NULL,}; 如果 dispatch 直接...
10 多态与转型:一个指针管所有LED
10 多态与转型:一个指针管所有LED C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED ⇐ 当前位置 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 现在我们已经有了两个零件:公共字段 base 和行为表 ops。多态要解决的问题:上层不想知道这颗 LED 到底是 GPIO、PWM 还是 RGB,只想写一句 led_on(led)。 先看问题:if/else 把具体类型泄漏到上层没有多态时,上层往往这样写: 1234567if (type == LED_TYPE_GPIO) { gpio_led_on(&gpio_led);} else if (type == LED_TYPE_PWM) { ...
09 ops/vtable:把一组函数指针装进对象
09 ops/vtable:把一组函数指针装进对象 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 ⇐ 当前位置 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 函数指针能替换一个动作。但一个对象通常不止一个动作——LED 有 on、off、set_brightness、deinit;文件有 open、read、write、release;网卡有 open、stop、start_xmit。 痛点:一个个传函数指针太散如果只用单个函数指针: 123led_run_action(led, gpio_led_on);led_run_action(led, gpio_led_off);led_run_set(led,...
08 函数指针:写死的函数怎么换
08 函数指针:写死的函数怎么换 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 ⇐ 当前位置 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 继承复用了公共字段,但还没有解决行为差异。GPIO LED 的开灯是写电平,PWM LED 的开灯是设占空比,RGB LED 可能要同时驱动三个通道。它们都叫”开灯”,但具体动作不同。 如果把动作写死: 1234void led_run_once(LedBase_t *led){ led_base_on(led);} 那这个函数只能执行固定动作。想支持关灯、翻转、闪烁,就要继续加函数。更好的方向是:对象不变,动作由调用方传进来。这就是函数指针的用处。 函数名本身可以表示函数地址先定义几个动作: 123int...
07 继承:struct嵌套消灭复制粘贴
07 继承:struct嵌套消灭复制粘贴 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 ⇐ 当前位置 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 封装解决了”一个对象怎么组织”的问题。但项目继续发展,你会遇到新的问题:多个对象有一半字段和函数都一样。比如 GPIO LED、PWM LED、呼吸灯、RGB LED,它们都有开关状态、亮度、初始化状态,但具体点亮方式不同。 痛点:三种LED,一半字段重复反面写法: 12345678910111213141516171819202122typedef struct { uint8_t pin; uint8_t brightness; bool is_on; bool initialized;}...
06 HAL映射:工业库也是同一套封装套路
06 HAL映射:工业库也是同一套封装套路 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 工业库也是同一套封装套路 ⇐ 当前位置 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 前面讲的 struct + me + static + init/deinit,你在 STM32 HAL 这类工业库里看到的 GPIO、UART、SPI、TIM,用的也是同一套设计——只是它们操作的不是一颗 LED 的软件状态,而是一组硬件寄存器。 先看问题:HAL 函数一堆,看完就忘很多人第一次看...
05 数据归位:你的全局变量,该死了
05 数据归位:你的全局变量,该死了 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 你的全局变量,该死了 ⇐ 当前位置 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 全局变量的问题,不是”看起来不优雅”。真正的问题是:它让数据没有主人。数据没有主人,谁都能改;谁都能改,就很难知道 bug 是谁带来的。 看一眼反面写法12345int g_pin = 0;int g_brightness = 0;int init_count = 0;int MAX_BRIGHTNESS = 255;int g_debug_flag = 0; 初始化函数: 1234567891011int bad_led_init(uint8_t pin){ g_pin = pin; g_brightness =...
04 手搓class:前缀、init/deinit和生命周期
04 手搓class:前缀、init/deinit和生命周期 C 语言 OOP 系列导航 C语言你真的会封装吗? 三个LED写了三份代码? 同事改一行,LED全乱了 手搓class:前缀、init/deinit和生命周期 ⇐ 当前位置 你的全局变量,该死了 工业库也是同一套封装套路 struct嵌套消灭复制粘贴 写死的函数怎么换 把一组函数指针装进对象 一个指针管所有LED 虚函数不实现会怎样 换硬件不改应用 从自动注册到Linux内核OOP全貌 C 语言没有 class,没有命名空间,也没有构造函数、析构函数。但工程代码不能因此乱写。 在 C 里,一个成熟模块通常靠三件事撑起”类”的样子:函数前缀、init/deinit、初始化状态检查。 痛点:所有模块都叫 init(),迟早撞名如果你有 LED 模块和电机模块,可能一开始会这样写: 123int init(void);int on(void);int off(void); 然后电机模块也想写: 123int init(void);int start(void);int...









