首页
学习
活动
专区
圈层
工具
发布

让我失眠3个月的嵌入式项目,对C语言的认知全推翻了

凌晨两点,盯着屏幕上的代码,工程师高培第三次把修改的LED控制逻辑恢复原状。不是因为改不对,是因为不敢改——每动一行代码,就像在拆一颗定时炸弹,不知道哪个模块会因此瘫痪。那是去年接手的一个维护项目,硬件是ARM Cortex-M3,产品已经在量产,客户等着加新功能。代码能跑,功能正常,看起来一切完美。但当我试图修改一个LED闪烁逻辑时,噩梦开始了:改完LED,串口打印乱了;把串口改回去,ADC采集又不对。一个LED的改动,波及了三个不相关的模块。

打开整个工程,我找到了原因:整个项目只有一个.c文件,8万行代码。8000行代码里用了500多个全局变量,flag1、flag2、temp_flag、flag_temp命名随意到让人绝望。更可怕的是,中断服务函数里直接修改这些全局变量,主循环里轮询判断,没有任何保护机制。有一次跟踪一个bug,发现一个变量在五个地方被修改:两个中断、三个函数。改这个变量的人,根本不知道还有谁在用。硬件驱动、协议栈、业务逻辑、UI显示全揉在一起,驱动层直接调用应用层函数,应用层直接操作寄存器。这就是典型的“大泥球”架构——看起来是个整体,实际上是一团乱麻。

另一个让我失眠的问题藏在一个延时函数里:void delay(int count) { for(int i = 0; i < count; i++); }。客户反馈设备上电后偶尔起不来,排查两周发现是编译器优化级别从-O0改成-Os后,这个延时函数被直接删掉了——循环变量i没有被使用,编译器认为这个循环“没有意义”。加上volatile后问题解决,但这件事让我后怕:还有多少代码正在被编译器默默“优化”掉?更离谱的是,有人在中断服务函数里调用printf调试。printf可能触发系统调用、可能阻塞、可能重入——每一个特性都和中断的“快速、简单、不可重入”原则冲突。问他为什么这么写,答:“调试方便啊。”

重构这个项目时,我给自己定了一条铁律:一个模块一个文件,一个文件不超过2000字。硬件抽象层放最底层,板级支持包放中间,应用层放最上面。接口通过头文件暴露,实现细节全部隐藏。真正的转折点是引入面向对象思想——用C语言。比如多个传感器,传统写法是switch(sensor_type),加一个新传感器就要改函数。用结构体封装操作函数指针后,新增传感器只需添加一个结构体实例,主循环一行不用改。Linux内核的设备驱动模型就是这个思路的放大版。

重构到一半,拉了两个同事做代码评审。第一次被挑出17个问题:变量命名不规范、边界条件没处理、函数太长、注释过时……改完后代码确实清爽了。后来团队规定每次提交必须有两人review,半年后回头看,代码质量提升了一个量级。重构后期我开始加防御性代码:检查所有指针参数,检查数组下标,检查函数返回值,用断言捕获“不可能发生”的情况。有个同事嫌啰嗦:“这个函数永远不会传NULL进来。”第二天,就传了。

那个项目最后花了三个月重构成清晰的分层结构,添加了单元测试,建立了代码评审流程。客户的新功能按时上线,运行稳定。后来新同事入职,看了代码说:“这个项目结构好清晰。”那一刻我觉得值了。现在回头看,那个让我失眠三个月的项目反而成了技术生涯的转折点。它让我明白:“能跑”的代码和“高质量”的代码之间,隔着一个太平洋。只有掌握了嵌入式C硬核的技术,才能够铸就工业级高质量的代码。嵌入式C语言编程,难的不是语法,不是指针,不是位操作——难的是构建一个能稳定运行、易于维护、敢于修改的系统。这需要技术,更需要方法论。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OWjxTyXYGZ4EAaKovBvXFF1A0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券