《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准

《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准

前言

在电子系统开发领域,C 语言作为底层开发的核心语言,其代码质量直接关系到系统的稳定性、可维护性和扩展性。良好的编码规范不仅是团队协作的基础,更是降低生命周期成本的关键。本规范融合业界最佳实践与工程实践经验,结合具体代码示例,从原则到实践进行全面阐述,旨在引导开发者写出高质量、易维护的代码。

一、代码工程化核心准则

1.1 可读性优先原则

代码的首要属性是 “被人理解”,而非 “被机器执行”。具备高可读性的代码应满足 “自文档化” 特征,即无需额外注释即可通过命名、结构推导其逻辑意图。软件生命周期中,80% 以上的时间用于维护与迭代,可读性是降低维护成本的核心。

业界数据:中小型电子系统(代码量 <10 万行)的维护成本约为开发阶段的 5-8 倍,大型系统(代码量> 100 万行)可达到 100 倍以上。因代码晦涩导致的理解偏差,占维护阶段错误总量的 60% 以上。

实践要点:在性能非瓶颈场景下,可读性优先级高于执行效率。例如,一段为追求极致性能而采用复杂指针运算的代码,若逻辑晦涩,后续维护引入 bug 的概率会大幅增加。

1.2 简洁性与冗余控制准则

代码简洁性体现在逻辑链路精简与实现方式直观。冗余代码(未调用的函数、重复逻辑块、过期注释)会增加代码体积,成为故障排查的 “噪声源”。

实践手段

  • 即时清除废弃代码:未被调用的全局变量、静态函数应在代码审查阶段删除。

  • 重复逻辑抽象:相同逻辑块出现 3 次及以上时,需抽象为独立函数或宏。

  • 模块化拆分:单文件代码量超过 1000 行时,按功能域拆分,每个子模块聚焦单一职责。

1.3 风格一致性原则

编码风格统一是团队协作的基础。电子系统开发涉及硬件驱动、协议栈、应用逻辑等多层面代码,风格混乱会降低跨层级调试效率。

实践要求

  • 新开发代码严格遵循团队规范(命名、缩进等)。

  • 重构 legacy 代码时,若风格冲突,优先用格式转换工具(如 Clang Format)标准化。

  • 第三方代码(如芯片驱动)保留原始风格,通过封装层隔离,避免污染自研代码。

二、头文件设计规范

头文件是模块接口的 “契约文书”,设计质量直接影响编译效率与模块解耦。不合理的头文件依赖可能导致编译时间延长 5-10 倍,引发跨模块错误。

2.1 头文件功能边界界定

头文件核心职能是 “声明接口”,而非 “实现逻辑”。

允许包含:对外函数原型(extern声明)、跨模块宏定义(#define)、类型别名(typedef)、枚举(enum)及结构体(struct)定义。

禁止包含:函数实现、全局变量定义(int g_var;)、静态变量(static int s_var;)、内部使用的宏 / 类型。

反例分析:某射频模块驱动头文件rf_driver.h包含static void rf_init_reg()实现,被 10 个文件引用时出现 10 次重复定义错误,最终将实现迁移至rf_driver.c解决。

2.2 头文件依赖管控

头文件依赖过深是编译效率低下的主因。一个核心头文件被过度包含可能导致项目编译时间增加数小时。

管控措施

  • 职责单一化:每个头文件聚焦单一功能域,如uart_proto.h仅含串口协议接口,不混入 I2C 定义。

  • 避免循环依赖:通过 “前向声明”(typedef struct XXX XXX;)打破a.h→b.h→c.h→a.h闭环。

  • 最小化包含:仅包含正常编译所需的头文件。

错误示例:某平台定义WORD类型的头文件包含大量无关头文件:

\#include \<VXWORKS.H>\#include \<KERNELLIB.H>\#include \<SEMLIB.H>// ... 包含20余个无关头文件typedef unsigned short WORD;

问题分析:工程中 10000 个源文件,100 个使用stdio.hprintf,因WORD是必包含类型,导致stdio.h等被不必要展开 9900 次,大幅增加编译时间。

量化指标:大型项目中,单个.c文件直接包含头文件控制在 5-8 个,间接包含不超过 20 个。

2.3 自包含与防护机制

头文件需满足 “自包含性”—— 单独包含即可编译。同时通过宏防护避免重复包含。

自包含实现:若a.h依赖b.h,则a.h应主动包含b.h,不要求使用者手动包含。

宏防护格式:采用PROJECT_MODULE_FILENAME_H命名,确保唯一性:

\#ifndef VOS\_INCLUDE\_TIMER\_TIMER\_H\#define VOS\_INCLUDE\_TIMER\_TIMER\_H// 头文件内容\#endif

或简单方式:

\#ifndef TIMER\_H\#define TIMER\_H// 头文件内容\#endif

例外情况:版权声明和整体注释可放在#ifndef之前。

2.4 其他头文件规范

  1. 禁止循环依赖:如a.h包含b.hb.h包含c.hc.h包含a.h,会导致任一修改引发大量重编译。

  2. 禁止包含无用头文件:避免图省事包含所有可能用到的头文件,或使用包含所有头文件的god.h,这会恶化编译时间,增加维护难度。

  3. 禁止在头文件定义变量:会因被多个.c包含导致重复定义。

  4. 通过头文件使用外部接口:禁止在.c中用extern声明外部函数 / 变量。例如a.c使用b.cfoo(),应在b.h声明,a.c通过#include <b.h>使用,避免声明与定义不一致。

  5. 禁止在extern "C"中包含头文件:可能导致嵌套层次超限或破坏头文件意图。

错误示例

extern "C" {\#include "xxx.h"// ...}

正确示例

\#include "xxx.h"extern "C" {// ...}

三、函数设计与实现规范

函数是代码逻辑的基本执行单元,设计质量影响可测试性与复用性。实时系统中,函数调用效率与堆栈占用关乎实时性。

3.1 功能单一性准则

每个函数应仅实现单一逻辑功能,职责边界可通过 “一句话描述” 界定。例如uart_send_byte(uint8_t data)仅 “通过 UART 发送一个字节”,不包含校验、缓冲区管理等附加逻辑。

量化约束:函数非空非注释行(NBNC)控制在 50 行内。算法类函数可放宽至 80 行,但需用注释明确逻辑分段。

3.2 可重入性设计

多任务系统中,函数可重入性是并发安全的基础。可重入函数需满足:

  • 不使用静态局部变量存储中间状态。

  • 访问共享资源时,通过互斥机制(信号量、关中断)保护。

错误示例:不可重入函数

int g\_exam;unsigned int example(int para) {&#x20;   unsigned int temp;&#x20;   g\_exam = para; // (\*\*)&#x20;   temp = square\_exam();&#x20;   return temp;}

问题分析:多线程调用时,(**)语句执行后若线程切换,新线程可能修改g_exam,导致temp结果错误。

正确示例:可重入函数改进

int g\_exam;SemaphoreHandle\_t sem; // 假设已初始化信号量unsigned int example(int para) {&#x20;   unsigned int temp;&#x20;   xSemaphoreTake(sem, portMAX\_DELAY); // 申请信号量&#x20;   g\_exam = para;&#x20;   temp = square\_exam();&#x20;   xSemaphoreGive(sem); // 释放信号量&#x20;   return temp;}

改进说明:通过信号量保证对共享变量g_exam的独占访问,避免并发冲突。

3.3 函数嵌套与长度控制

  1. 避免函数过长:新增函数非空非注释行不超过 50 行。过长函数往往功能不单一、过于复杂。

  2. 避免嵌套过深:新增函数代码块嵌套不超过 4 层。每级嵌套增加阅读脑力消耗,需功能分解。

错误示例:嵌套深度 5 层

void serial(void) {&#x20;   if (!Received) {&#x20;       TmoCount = 0;&#x20;       switch (Buff) {&#x20;           case AISGFLG:&#x20;               if ((TiBuff.Count > 3) &&&#x20;&#x20;                   ((TiBuff.Buff\[0] == 0xff) || (TiBuf.Buff\[0] == CurPa.ADDR))) {&#x20;                   Flg7E = false;&#x20;                   Received = true;&#x20;               } else {&#x20;                   TiBuff.Count = 0;&#x20;                   Flg7D = false;&#x20;                   Flg7E = true;&#x20;               }&#x20;               break;&#x20;           default:&#x20;               break;&#x20;       }&#x20;   }}

问题分析:多层嵌套使逻辑晦涩,阅读者需记住多层上下文,易出错。

3.4 参数与返回值规范

  • 参数数量:不超过 5 个,超过时封装为结构体。如uart_config(UART_InitTypeDef *init)替代多参数版本。

  • 常量参数:输入参数无需修改时声明为const,如void log_print(const char *fmt, ...),避免意外修改。

  • 返回值处理:错误码需被显式处理,禁止忽略。如if (uart_send(data) != UART_OK) { /* 错误处理 */ }

正确示例const参数使用(C99 标准strncmp

int strncmp(const char \*s1, const char \*s2, register size\_t n) {&#x20;   register unsigned char u1, u2;&#x20;   while (n-- > 0) {&#x20;       u1 = (unsigned char) \*s1++;&#x20;       u2 = (unsigned char) \*s2++;&#x20;       if (u1 != u2) {&#x20;           return u1 - u2;&#x20;       }&#x20;       if (u1 == '\0') {&#x20;           return 0;&#x20;       }&#x20;   }&#x20;   return 0;}

说明s1s2为输入参数,声明为const确保不被修改,增强代码安全性。

3.5 其他函数规范

  1. 避免使用全局变量、静态局部变量和 I/O 操作:此类函数功能可能不可预测,不利于测试维护。

错误示例:使用静态局部变量导致功能不可预测

unsigned int integer\_sum(unsigned int base) {&#x20;   unsigned int index;&#x20;   static unsigned int sum = 0; // static变量导致状态保留&#x20;   for (index = 1; index <= base; index++) {&#x20;       sum += index;&#x20;   }&#x20;   return sum;}

问题分析sum值依赖调用历史,多次调用结果不一致,如integer_sum(2)首次返回 3,再次调用返回 6。

  1. 函数参数个数不超过 5 个:过多参数易受外部变化影响,增加测试工作量,超过时建议拆分函数。

  2. 除打印类函数外,不使用可变长参函数:处理复杂易出错,性能低,增加维护难度。

  3. 源文件内函数除非外部可见,否则加static:确保仅在声明文件可见,避免标识符冲突。

正确示例STATIC宏定义

\#ifdef \_DEBUG\#define STATIC static\#else\#define STATIC\#endif

说明:调试阶段定义为static,发布时为空,便于后续打热补丁。

四、标识符命名体系

标识符(变量、函数、宏等)命名是代码可读性的 “第一视觉要素”。电子系统中,命名混乱可能导致硬件寄存器与软件变量混淆,引发致命错误。

4.1 命名语义性原则

  • 变量命名:“名词 + 修饰词” 结构,反映存储内容,如uint8_t uart_rx_buf[32](UART 接收缓冲区),避免uint8_t buf[32]

  • 函数命名:“动词 + 名词” 结构,体现执行动作,如adc_start_conversion(),而非adc_do()

  • 常量命名:全大写 + 下划线,如#define ADC_MAX_VALUE 4095,枚举同理。

正确示例

int error\_number;int number\_of\_completed\_connection;

错误示例

int n;          // 含义模糊int nerr;       // 缩写不清晰int n\_comp\_conns; // 缩写混乱

4.2 风格统一性与环境适配

  • 平台适配:芯片厂商驱动保留原始风格(如 TI 的HAL_UART_Transmit),自研代码统一风格(如uart_transmit)。

  • 缩写规范:仅用业界公认缩写(buf=buffer,cfg=configuration),禁止自创缩写。

常见缩写示例

  • arg=argument,buff=buffer,clk=clock

  • cmd=command,cmp=compare,cfg=configuration

  • dev=device,err=error,hex=hexadecimal

4.3 作用域标识

  • 全局变量:前缀g_,如g_system_time

  • 静态变量:前缀s_,如static uint32_t s_uart_tx_cnt

  • 局部变量:无特殊前缀,禁止单字母命名(ijk作为循环变量除外)。

4.4 其他命名规范

  1. 用反义词组命名互斥元素:如add/removecreate/destroylock/unlock等。

  2. 避免名字中出现数字编号:除非逻辑必需。

错误示例

\#define EXAMPLE\_0\_TEST\_\#define EXAMPLE\_1\_TEST\_

正确示例

\#define EXAMPLE\_UNIT\_TEST\_\#define EXAMPLE\_ASSERT\_TEST\_
  1. 标识符前不加模块 / 项目前缀:避免文件名不可读、过长及移植困难。

  2. 不建议使用匈牙利命名法:变量名应说明含义而非类型,否则修改类型时需大量改动。

五、变量与数据类型规范

变量是代码运行时状态的载体,其定义与使用影响稳定性与可移植性。嵌入式系统中,类型不匹配可能导致寄存器操作异常。

5.1 变量功能单一性

一个变量仅用于存储单一逻辑含义的数据,禁止 “一变量多用途”。

错误示例:变量多用途

WORD DelRelTimeQue(void) {&#x20;   WORD Locate;&#x20;   Locate = 3;&#x20;&#x20;   Locate = DeleteFromQue(Locate); // 既表位置又表返回值&#x20;   return Locate;}

正确示例:拆分变量

WORD DelRelTimeQue(void) {&#x20;   WORD Ret;&#x20;   WORD Locate;&#x20;   Locate = 3;&#x20;   Ret = DeleteFromQue(Locate);&#x20;   return Ret;}

5.2 结构设计规范

结构应功能单一,代表一种现实事务的抽象,各元素应是同一事务的不同侧面。

错误示例:结构职责不单一

typedef struct STUDENT\_STRU {&#x20;   unsigned char name\[32]; /\* 学生姓名 \*/&#x20;   unsigned char age; /\* 学生年龄 \*/&#x20;   unsigned char sex; /\* 学生性别 \*/&#x20;   unsigned char teacher\_name\[32]; /\* 老师姓名 \*/&#x20;   unsigned char teacher\_sex; /\* 老师性别 \*/} STUDENT;

问题分析:结构同时包含学生和老师信息,职责混乱。

正确示例:拆分结构

typedef struct TEACHER\_STRU {&#x20;   unsigned char name\[32]; /\* 老师姓名 \*/&#x20;   unsigned char sex; /\* 老师性别 \*/&#x20;   unsigned int teacher\_ind; /\* 老师索引 \*/} TEACHER;typedef struct STUDENT\_STRU {&#x20;   unsigned char name\[32]; /\* 学生姓名 \*/&#x20;   unsigned char age; /\* 学生年龄 \*/&#x20;   unsigned char sex; /\* 学生性别 \*/&#x20;   unsigned int teacher\_ind; /\* 关联老师索引 \*/} STUDENT;

5.3 全局变量管控

全局变量是模块间耦合的主要来源,应严格限制使用:

  1. 必要性论证:优先通过函数参数 / 返回值替代。

  2. 访问控制:需通过接口函数访问,禁止直接读写。如风扇管理模块提供SetFanWorkModeGetFanSpeed等接口。

  3. 初始化顺序:明确初始化依赖,避免跨模块依赖导致未初始化访问。

5.4 其他变量规范

  1. 防止局部变量与全局变量同名:虽不报错,但易误解。

  2. 通讯结构注意字节序:跨平台交互时,数据成员发送前需主机序转网络序,接收后网络序转主机序。

  3. 严禁使用未初始化变量作为右值:首次使用前初始化,初始化位置离使用越近越好。

  4. 减少不必要的类型转换:强制转换可能改变数据意义和取值,易留隐患。

错误示例:类型转换隐患

char ch;unsigned short int exam;ch = -1;exam = ch; // 编译器不告警,exam值为0xFFFF(因char为有符号,转换为无符号时扩展符号位)

六、宏与常量规范

宏与常量是固定逻辑与数值的抽象,定义质量影响可维护性与安全性。嵌入式系统中,宏定义错误可能导致硬件配置错误。

6.1 宏定义安全性

  1. 表达式用完备括号包裹:宏是简单替换,需避免运算优先级问题。

错误示例

\#define RECTANGLE\_AREA(a, b) a \* b\#define RECTANGLE\_AREA(a, b) (a \* b)\#define RECTANGLE\_AREA(a, b) (a) \* (b)

问题分析

  • 第一个宏:c/RECTANGLE_AREA(a,b)展开为c/a*b,运算顺序错误。

  • 第二个宏:RECTANGLE_AREA(c+d,e+f)展开为(c+d*e+f),优先级错误。

正确示例

\#define RECTANGLE\_AREA(a, b) ((a) \* (b))
  1. 宏参数不允许变化:避免参数包含自增 / 自减,防止多次展开导致非预期结果。

错误示例

\#define SQUARE(a) ((a) \* (a))int a = 5;int b;b = SQUARE(a++); // 展开为(a++)\*(a++),a最终为7(执行两次增)

正确示例

b = SQUARE(a);a++; // a最终为6(仅一次增)
  1. 宏定义多条表达式放大括号中:确保作为一个整体执行。

6.2 常量替代宏

优先用const定义常量,便于编译器类型检查。

问题示例:宏的缺陷

\#define ASPECT\_RATIO 1.653 // 编译器看不到ASPECT\_RATIO,报错信息显示1.653,难以追踪

正确示例const常量

const double ASPECT\_RATIO = 1.653; // 编译器可见,报错信息明确

字符串常量示例

const char \* const authorName = "Scott Meyers"; // 指针和指向内容均为常量

6.3 消除魔鬼数字

具有业务含义的数字需定义为常量或宏,禁止硬编码。

解决途径

  • 局部唯一含义数字:可加注释或定义局部const变量。

  • 广泛使用数字:定义const全局变量 / 宏,命名自注释。

正确示例

\#define MAX\_CONN\_COUNT 1000 // 替代硬编码1000if (conn\_cnt > MAX\_CONN\_COUNT) { /\* 处理逻辑 \*/ }

6.4 优先使用函数代替宏

宏对比函数的缺点:

  • 缺乏类型检查。

  • 可能产生副作用(如参数自增)。

  • 难以调试和断点。

  • 多次调用浪费代码空间。

错误示例:宏与函数的差异

\#define MAX\_MACRO(a, b) ((a) > (b) ? (a) : (b))int MAX\_FUNC(int a, int b) {&#x20;   return ((a) > (b) ? (a) : (b));}int testFunc() {&#x20;   unsigned int a = 1;&#x20;   int b = -1;&#x20;   printf("MACRO: max of a and b is: %d\n", MAX\_MACRO(++a, b));&#x20;   printf("FUNC : max of a and b is: %d\n", MAX\_FUNC(a, b));&#x20;   return 0;}

输出结果

MACRO: max of a and b is: -1 // 宏无类型检查,a和b按无符号比较(-1为0xFFFFFFFF)FUNC : max of a and b is: 2 // 函数有类型检查,正确比较

6.5 宏定义避免改变程序流程的语句

宏中使用returngoto等可能导致资源泄漏,使用者难察觉。

错误示例:宏导致内存泄漏

\#define CHECK\_AND\_RETURN(cond, ret) {if (cond == NULL\_PTR) {return ret;}}// 使用场景pMem1 = VOS\_MemAlloc(...);CHECK\_AND\_RETURN(pMem1, ERR\_CODE\_XXX)pMem2 = VOS\_MemAlloc(...);CHECK\_AND\_RETURN(pMem2, ERR\_CODE\_XXX) // 若pMem2为NULL,pMem1未释放导致泄漏

七、表达式与语句规范

表达式是逻辑执行单元,清晰度影响可调试性。实时系统中,表达式复杂度可能影响执行效率。

7.1 表达式可读性

  1. 括号显式化:明确运算顺序,避免依赖默认优先级。

清晰示例

if ((a > b) && (c <= d)) { ... }

模糊示例

if (a > b && c <= d) { ... } // 依赖运算符优先级,易误解
  1. 禁止嵌套函数调用:函数参数不应包含函数调用,避免调试困难。

错误示例

printf("%d, %d", fun1(), fun2()); // 调用顺序不确定,调试困难

正确示例

int res1 = fun1();int res2 = fun2();printf("%d, %d", res1, res2);

7.2 控制语句规范

  1. 赋值语句不放在if等语句中或作为函数参数if中前序条件满足时,后续条件不执行,可能导致赋值未执行。

错误示例

int main() {&#x20;   int a = 0;&#x20;   int b;&#x20;   if ((a == 0) || ((b = fun1()) > 10)) { // 因a==0为真,b=fun1()不执行&#x20;       printf("a: %d\n", a);&#x20;   }&#x20;   printf("b: %d\n", b); // b未初始化,行为未定义}
  1. 赋值操作符不用于产生布尔值的表达式

正确示例

x = y;if (x != 0) {&#x20;   foo();}

错误示例

if ((x = y) != 0) { foo(); }// 更差if (x = y) { foo(); }
  1. if/for/while等语句独占一行:代码块用{}包裹,即使一行代码。

  2. switch-case处理:非连续case需加break;需穿透执行时,加注释// 穿透至下一个case

示例

case CMD\_FWD:&#x20;   ProcessFwd();&#x20;   /\* now jump into case CMD\_A \*/case CMD\_A:&#x20;   ProcessA();&#x20;   break;// 连续case无处理时无需注释switch (cmd\_flag) {&#x20;   case CMD\_A:&#x20;   case CMD\_B: {&#x20;       ProcessCMD();&#x20;       break;&#x20;   }&#x20;   // ...}

八、注释体系规范

注释是代码的 “辅助说明系统”,核心是解释 “为何如此设计”,而非重复 “做什么”。电子系统中,硬件相关注释(如寄存器配置原因)尤为重要。

8.1 注释层级

  1. 文件头注释:包含版权信息、版本历史、功能概述、接口清单、作者信息等。

  2. 函数注释:包含功能描述、参数含义、返回值说明、异常处理、调用约束(如 “需在中断上下文调用”)。

  3. 代码块注释:针对复杂逻辑(算法步骤、硬件时序控制),说明设计思路与关键节点。

8.2 优秀代码的自解释性

优秀代码无需注释即可读懂,注释无法弥补糟糕代码的缺陷。

错误示例:需大量注释的糟糕代码

/\* 判断m是否为素数\*//\* 返回值:1是素数,0不是素数\*/int p(int m) {&#x20;   int k = sqrt(m);&#x20;   for (int i = 2; i <= k; i++)&#x20;       if (m % i == 0)&#x20;           break; /\* 发现整除,表示m不为素数,结束遍历\*/&#x20;   /\* 遍历中没有发现整除的情况,返回1\*/&#x20;   if (i > k)&#x20;       return 1;&#x20;   /\* 遍历中发现整除的情况,返回0\*/&#x20;   else&#x20;       return 0;}

重构后:自解释代码

int IsPrimeNumber(int num) {&#x20;   int sqrt\_of\_num = sqrt(num);&#x20;   for (int i = 2; i <= sqrt\_of\_num; i++) {&#x20;       if (num % i == 0) {&#x20;           return FALSE;&#x20;       }&#x20;   }&#x20;   return TRUE;}

8.3 其他注释规范

  1. 注释内容清晰准确:避免二义性,否则反而误导维护者。

  2. 注释与代码同步:修改代码时更新相关注释,删除无用注释,不保留注释掉的代码(可从版本库找回)。

  3. 全局变量注释:详细说明功能、取值范围及存取注意事项。

示例

/\* SCCP转换时的错误码 \*//\* 全局标题解析失败,取值如下 \*/ /\* 变量作用 \*//\* 0-成功 1-GT表错误 2-GT错误 其他-未使用 \*/ /\* 取值范围 \*//\* 仅本模块的SCCPTranslate()可修改,其他模块通过GetGTTransErrorCode()访问 \*/ /\* 使用方法 \*/BYTE g\_GTTranErrorCode;
  1. 注释位置:放在代码上方相邻位置或右方,放上方时与上面代码用空行隔开,缩进与下方代码一致。

示例

/\* 活动统计任务数量 \*/\#define MAX\_ACT\_TASK\_NUMBER 1000// 枚举注释/\* SCCP与用户交互的原语消息名 \*/enum SCCP\_USER\_PRIMITIVE {&#x20;   N\_UNITDATA\_IND, /\* SCCP通知用户有单元数据到达 \*/&#x20;   N\_NOTICE\_IND,   /\* SCCP通知用户七号网无法传输此消息 \*/&#x20;   N\_UNITDATA\_REQ  /\* 用户请求SCCP传输单元数据 \*/};
  1. 注释风格统一:同一产品 / 项目组统一风格,优先使用中文(国内团队),采用工具可识别格式(如 doxygen)。

九、排版与格式规范

代码排版是可读性的 “视觉骨架”,统一排版减少跨团队协作成本。

9.1 缩进与空行

  1. 缩进:每级缩进 4 个空格(禁止用制表符\t,避免编辑器差异)。

  2. 空行分隔:逻辑独立的代码块之间、变量声明与代码执行之间加空行。

错误示例

if (!valid\_ni(ni)) {&#x20;   // 代码块&#x20;   ...}repssn\_ind = ssn\_data\[index].repssn\_index;repssn\_ni = ssn\_data\[index].ni;

正确示例

if (!valid\_ni(ni)) {&#x20;   // 代码块&#x20;   ...}repssn\_ind = ssn\_data\[index].repssn\_index;repssn\_ni = ssn\_data\[index].ni;

9.2 语句格式

  1. 一行一语句:禁止多个语句写在同一行。

错误示例

int a = 5; int b = 10; // 糟糕排版

正确示例

int a = 5;int b = 10;
  1. 长语句拆分:超过 120 字符需拆分,新行缩进一级,拆分处选低优先级运算符,操作符放新行首。

正确示例

if ((temp\_flag\_var == TEST\_FLAG)&#x20;   && (((temp\_counter\_var - TEST\_COUNT\_BEGIN) % TEST\_COUNT\_MODULE) >= TEST\_COUNT\_THRESHOLD)) {&#x20;   // 处理代码}
  1. 关键字与空格ifforwhile等与括号间加空格;双目操作符前后加空格;单目操作符前后不加空格;->.前后不加空格。

正确示例

// 双目操作符空格if (current\_time >= MAX\_TIME\_VALUE)a = b + c;a \*= 2;// 单目操作符无空格\*p = 'a';flag = !is\_empty;p = \&mem;i++;// ->无空格p->id = pid;// 关键字与括号间空格if (a >= b && c > d)

十、代码构建与质量管控

编译与静态检查是质量保障的 “第一道防线”,嵌入式系统中,构建规范性直接影响固件可靠性。

10.1 编译配置

  1. 开启最高告警级别:理解所有告警,通过修改代码而非降低级别消除告警。编译器告警常提示潜在问题。

  2. 统一编译开关:团队内统一编译开关、静态检查选项及告警清除策略。必须禁用告警时,尽可能局部禁用并加注释说明原因。

10.2 版本控制

  1. 本地与 CI 配置一致:本地构建工具(如 PC-Lint)配置与持续集成一致,避免本地构建通过但 CI 失败。

  2. 及时签入代码:使用版本控制系统,及时签入通过本地构建的代码,确保不影响整体构建。

  3. 谨慎使用块拷贝:避免块拷贝导致的代码冗余或逻辑错误。

结语

本规范是电子系统开发 “工程化思维” 的体现,实际应用中需结合项目规模、团队构成、硬件约束灵活调整。核心目标是通过标准化提升代码质量,降低生命周期成本,实现 “一次编写,长期可靠” 的工程愿景。遵循规范不仅是技术要求,更是职业素养的体现,有助于打造高效协作的开发团队和高质量的软件产品。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93105.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

纯半精度模型和全精度模型的耗时分别为248微秒和1400微秒。混合精度模型371微秒比原始模型快大约四倍!

不过有一点需要注意:在上下文管理器内部生成的任何输出,必然会采用该上下文管理器的数据类型。因此,之后我们必须将这些输出转换回FP32(例如,使用float()函数)。 with torch.autocast(device_type="cuda", dtype=torch.float16): res16 = mixed32(torch.randn…

一款开源的远程桌面软件,旨在为用户提供流畅的游戏体验,支持 2K 分辨率、60 FPS,延迟仅为 40ms。

软件介绍 CloudPlayPlus&#xff08;云玩加&#xff09;是一款令人惊艳的开源远程桌面、串流软件&#xff0c;云玩加由个人开发者开发者&#xff0c;具有四大特征&#xff1a;开源、免费、低延迟、安全。 软件使用 客户端支持多个平台&#xff0c;包括 Windows、Mac OS、安卓…

MySql——binlog和redolog的区别

目录一、binlog和redolog的区别一、binlog和redolog的区别 binlog和redolog都是存储修改的新数据&#xff0c;是否保留binlog和redolog中的一个即可。 binlog属于整个mysql&#xff0c;是所有引擎共用的&#xff0c;不是只属于innoDB引擎。而redolog属于InnoDB存储引擎。binlo…

软件著作权产生与登记关键点

知识讲解一、 软件著作权的核心特征与权利内容自动产生原则&#xff1a; 这是软件著作权最核心、最重要的特征。产生时间&#xff1a; 软件著作权自软件开发完成之日起自动产生。法律依据&#xff1a; 《中华人民共和国著作权法》第二条及《计算机软件保护条例》第五条明确规定…

什么是主成分分析(PCA)和数据降维

主成分分析&#xff08;PCA&#xff09;和数据降维是机器学习和统计学中处理高维数据的核心工具。下面用清晰的结构解释其概念、原理和应用&#xff1a; 一、数据降维&#xff08;Dimensionality Reduction&#xff09; 1. 是什么&#xff1f; 目标&#xff1a;将高维数据&…

图论(4)单源赋权最短路径算法实现(BFS实现)

目录 1. 什么是赋权最短路径 2. 赋权最短路径中的关键概念 3. Dijkstra 算法的基本思想 4. Dijkstra 算法实现&#xff08;Java&#xff09; 1. 什么是赋权最短路径 在图论中&#xff0c;最短路径问题是指在图中寻找两点之间路径总权重最小的路径问题。如果图的每条边都带…

【Lua】题目小练9

题目&#xff1a;实现一个简单的“银行账户”类要求&#xff1a;使用 元表 模拟面向对象。支持以下功能&#xff1a;Account:new(owner, balance) 创建账户&#xff08;初始余额可选&#xff0c;默认为 0&#xff09;。deposit(amount) 存款&#xff08;不能为负数&#xff09;…

【二分图】染色问题

核心思想&#xff1a;为每一个未染色的&#xff0c;对它自己和它的邻居进行染色&#xff0c;看是否会出现冲突时间复杂度O&#xff08;nm&#xff09;#include<bits/stdc.h> using namespace std; using lllong long; const int N200010; int n,m; vector<int>edge…

报数游戏(我将每文更新tips)

今日tips&#xff1a;报数游戏题目描述报数游戏的游戏规则如下&#xff1a;对一个区间内的整数进行报数&#xff0c;若遇到的数字是质数或个位数是 1&#xff0c;则不报数&#xff0c;输出 pass。 给定开始游戏的第一个整数 a&#xff0c;及结束游戏时的最后一个整数 b&#xf…

大模型开发 - 基于Spring AI 借助MCP Client 通过STDIO和SSE协议调用MCP Server (上)

文章目录概述MCP协议&#xff1a;为AI应用连接外部世界的桥梁MCP Server&#xff1a;上下文与能力的提供者基于Spring AI 1.0.0的开发之路1. 使用Spring AI构建MCP客户端2. 使用Spring AI构建MCP服务器Mcp Client 实战整体架构概览技术栈Codepom配置mcp servers(sse&stdio)…

分析三个文件--启动文件、链接文件、map文件

目录 启动文件 链接文件 部分map文件内容 FLASH物理地址(0x08000000开始)的映射关系 0x08000000 之前地址空间 启动文件 ;******************** (C) COPYRIGHT 2016 STMicroelectronics ******************** ;* File Name : startup_stm32f40_41xxx.s ;* Author…

从零开始学Python之数据结构(字符串以及数字)

一、字符串 1.1 怎么定义字符串 字符串是Python最常用的数据结构之一。在 Python 里是用于处理文本数据的&#xff0c;比如存储姓名、文章内容等文本信息 。 定义方式&#xff1a; 单引号&#xff1a;用单引号 包裹文本&#xff0c;如 name Alice &#xff0c;单引号内可…

Navicat 全量增量数据库迁移

在使用 Navicat 进行数据库迁移时&#xff0c;除了常见的“全量迁移”&#xff08;一次性迁移所有数据和结构&#xff09;&#xff0c;有时还需要支持 增量迁移&#xff08;只迁移新增或修改的数据&#xff09;。下面我将详细讲解如何通过 Navicat 实现&#xff1a;&#x1f50…

css初学者第五天

<1>css的三大特性1.1 层叠性相同选择器给设置相同的样式&#xff0c;此时一个样式就会覆盖&#xff08;层叠&#xff09;另一份冲突的样式。层叠式主要解决样式冲突的问题。层叠性原则&#xff1a;-样式冲突&#xff0c;遵循的原则是就近原则&#xff0c;哪个样式离结构近…

从神经网络语言模型(NNLM)到Word2Vec:自然语言处理中的词向量学习

语言模型 语言(人说的话)模型(完成某个任务) 任务: 概率评估任务:在两句话中&#xff0c;判断哪句话出现的概率大(哪句话在自然语言中更合理)生成任务:预测词语,我明天要____ 统计语言模型 用统计的方法解决上述的两个任务 核心思想 给定一个词序列&#xff0c;计算该序列出现的…

PID学习笔记5-双环PID

在学习江协科技PID课程时&#xff0c;做一些笔记&#xff0c;对应视频3-1&#xff0c;对应代码&#xff1a;1313-双环PID定速定位置控制-代码封装main.c:#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLE…

C#vb.net中Interlocked类实现原子操作加减计算,涵盖状态切换、计数控制等常见场景

以下是 C# 中使用 int 类型结合 Interlocked 类实现原子操作的完整示例&#xff0c;涵盖状态切换、计数控制等常见场景&#xff1a; 完整代码示例csharp using System; using System.Threading;/// <summary> /// 基于整数类型的原子操作工具类&#xff08;线程安全&am…

RCL 2025 | LLM采样机制的新视角:来自处方性偏移的解释

1. 导读 大型语言模型&#xff08;Large Language Models, LLMs&#xff09;在自主决策场景中的应用日益广泛&#xff0c;它们需要在庞大的行动空间中进行响应采样&#xff08;response sampling&#xff09;。然而&#xff0c;驱动这一采样过程的启发式机制仍缺乏深入研究。本…

08 ABP Framework Blazor UI

ABP Framework Blazor UI 架构 overview ABP Blazor UI 系统构建在 Blazorise 组件库之上&#xff0c;为构建数据驱动应用提供结构化方法&#xff0c;包含 CRUD 操作、主题和本地化的一致模式。 #mermaid-svg-QAvWlELsLhZgYXHu {font-family:"trebuchet ms",verdana,…

JUC学习笔记-----LinkedBlockingQueueConcurrentLinkedQueueCopyOnWriteArrayList

LinkedBlockingQueue基本的入队出队初始化public class LinkedBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {// 静态内部类 Node&#xff0c;用于存储队列元素及维护节点间关系static class Node<E>…