MCU嵌入式C代码编码规范

时间:2025-02-15 07:17:59

  

目次. I

前言. II

1 规范制定说明. 1

2 适用范围. 1

3 基本原则. 1

4 组织构成及排版要求. 1

4.1组织构成及包含内容. 1

4.2 文件书写的层次. 1

4.3 排版要求. 2

5 注释. 3

5.1文件头和函数头. 3

5.2 注释. 4

6 命名规则. 5

6.1 命名规则总则. 5

6.2 变量命名规则. 6

6.3文件及函数命名. 7

6.4新定义的类型命名规范. 7

7 变量、常量、宏定义与类型. 7

8.指针和数组、结构与联合. 8

9 初始化、声明和定义. 9

9.1初始化. 9

9.2声明和定义. 9

10 控制语句和表达式. 10

11 函数. 10

12 预处理指令. 11

13 其余规则. 11

  

为提高产品代码质量,指导嵌入式软件开发人员编写出简洁、可维护、可靠、可测试、高效、可移植的代码,避开危险的编程方式,编写了本规范。

本标准由XX公司  XX部提出。

本标准由XX公司  XX部归口。 

本标准起草单位:

本标准主要起草人:

本标准主要修改人:

参考《MISRA 2004编码规则》。

编制

校对

审核

标准

批准

批准日期

XX

XX

嵌入式编程手写C代码编码规范

1 规范制定说明

本规范制定的意义在于提高代码的可读性,使代码标准化,为代码的集成、测试、维护、审查提供便利。

本规范作用在于约束代码编写规则,宗旨是使得代码简洁明了,可读性及可移植性强、易于维护。

本规范给出嵌入式软件C语言编码格式要求,并从排版要求、注释要求、命名规则、变量常量宏定义及类型、初始化、声明及定义、控制语句与表达式等方面详细给出编码规则,便于C语言编码人员及代码静态测试人员使用。

2 适用范围

本规范适用于XX公司嵌入式编程手写C代码开发,如VCU控制器手写代码部分的开发。

3 基本原则

1)代码简洁、语句清晰,风格统一。     

2)注释清晰明了,便于阅读。

3)代码结构清晰,可读性强,便于维护与测试。

4)模块化编程,尽量少用全局变量,避免使用GOTO语句。

5)通俗易懂不产生歧义。

4 组织构成及排版要求

4.1组织构成及包含内容

嵌入式代码源代码中包含头文件(.h)及源文件(.c),包含的内容为:

1)头文件:文件头说明、提供给外部参考的类型、常量、宏定义、(全局)函数声明、全局变量的原型声明,不要定义变量。

2)源文件:文件头说明、函数头说明、只在本文件中使用的类型、常量、宏定义及全局变量和文件级(static)变量定义。

4.2 文件书写的层次

1)实现文件(.C文件)的层次:

文件头

#include (依次为标准库头文件,非标准库头文件)

文件内部常量定义

文件内部宏定义

文件内部数据类型

文件内部静态全局变量

函数原型定义

2)声明文件(.H文件)的层次:

文件头

#ifndef _文件名_H (全大写)

#define _文件名_H (全大写)

其他条件编译选项

#include (依次为标准库头文件,非标准库头文件)

全局常量声明

全局宏声明

全局数据类型声明

全局变量声明

外部引用

全局函数原型声明

#endif  /* _文件名_H */

注意事项:

  1. 头文件尽量通过宏定义避免重复包含。
  2. 包含标准库头文件使用尖括号“<>”,包含非标准库头文件使用双引号“” ””。

4.3 排版要求

  1. 程序块采用缩进风格编写,缩进的空格数为4个,缩进采用空格键,不使用tab键。
  2. 相对独立的程序块之间、变量说明后必须加空行。
  3. 代码行不宜过长,较长的语句(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

eg:act_task_table[taskno].duration_true_or_false

              = SYS_get_sccp_statistic_state( stat_item );

  1. 若函数或过程中的参数较长,则要进行适当的划分。函数调用和函数声明续行在第一个参数处对齐,类型与名称不能分行书写。

eg:

n7stat_flash_act_duration( stat_item, frame_id *STAT_TASK_CHECK_NUMBER

                                           + index, stat_object );

  1. 在函数体的开始、结构体的定义、联合体的定义、枚举的定义以及if、for、do、while、switch、case语句中的程序都要采用程序块的分节符“{”“}”,分节符应独占一行且位于同一列,同时与引用它的语句左对齐。
  2. if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。

eg:

如下例子不符合规范。

if (pUserCR == NULL) return;

应写作:

if (pUserCR == NULL)

{

               return;

}

  1. do while使用时,while不独占一行。
  2. 定义指针类型变量时,*放在变量名前。
  3. 一行只允许写一个声明或一条语句。

eg:

= 0;  = 0;

应写作:

= 0; 

= 0;

  1. 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。
  1. 逗号、分号只在后面加空格。

eg:

int a, b, c;

  1. 比较操作符, 赋值操作符"="、 "+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",位域操作符"<<"、"^"等双目操作符的前后加空格。

eg:

if (current_time >= MAX_TIME_VALUE)

a = b + c;

a *= 2;

a = b ^ 2;

  1. "!"、"~"、"++"、"--"、"&"(地址运算符)等单目操作符前后不加空格。

eg:

*p = 'a';        // 内容操作"*"与内容之间

flag = !isEmpty; // 非操作"!"与内容之间

p = &mem;        // 地址操作"&" 与内容之间

i++;             // "++","--"与内容之间

  1. "->"、"."前后不加空格。

eg:

p->id = pid;     // "->"指针前后不加空格

  1. if、for、while、switch等与后面的括号间应加空格,使if等关键字更为突出、明显。

eg:

if (a >= b && c > d)

5 注释

5.1文件头和函数头

  1. 源文件(包括说明性如:头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应放置注释文字,注释必须列出:版权说明、版本号、完成日期、作者、内容、模块目的/功能、主要函数及其功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。

可参照如下源文件头注释,但是不局限于此。

/************************************************

File Name:BCU_TLE8104E_Drive.c

Author:   yangdan

Version:    v1.00.0002

Comment:   TLE8104E应用相关的程序。

Function List :

 1.————

History:

v1.00.0000              20171021  初始版本

************************************************/

  1. 函数头部也应进行注释,注释时要列出:函数名、函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等(必要时要举例说明函数的使用方法)。可参照如下函数头进行注释,但不局限于此。

/***************************************************************

Function:          Std_ReturnType QSPI2_ReadWriteData_TLE8104E(Spi_DataType *TxDataBufferPtr,Spi_DataType *RxDataBufferPtr,Spi_SequenceType SpiConf_SpiSequence,Spi_NumberOfDataType Length)

Description: QSPI读写函数

Input:                   Spi_DataType* TxDataBufferPtr----发送的数据

                             Spi_DataType* RxDataBufferPtr----接收的数据

                             Spi_SequenceType SpiConf_SpiSequence----QSPI队列

                             Spi_NumberOfDataType ui8_DataLength----发送接收的数据长度

Output:

Return:                 E_OK/E_NOT_OK

Time:

Other:          QSPI2_ReadWriteData_TLE8104E(tx_buff,rx_buff,SpiConf_SpiSequence_TLE8104E[TLE8104E_ChipId],1)

***********************************************************************/

5.2 注释

  1. 注释率要求>20%。
  2. 注释的内容要清楚明了、含义准确,防止注释二义性。
  3. 注释中避免使用缩写。
  4. 对单条语句的注释应放在其上方或右方相邻位置,如放于上方则需与其上面的代码用空行隔开。
  5. 对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方相邻位置或右方。
  6. 数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方;可按如下形式说明枚举/数据/联合结构。

/* sccp interface with sccp user primitive message name */

enum  SCCP_USER_PRIMITIVE

{

    N_UNITDATA_IND, /* sccp notify sccp user unit data come */

    N_NOTICE_IND,   /* sccp notify user the No.7 network can not */

                    /* transmission this message */

    N_UNITDATA_REQ, /* sccp user's unit data transmission request*/

};

  1. 全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。
  2. 分支语句(条件分支、循环语句等)必须编写注释。
  3. 对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进入下一个case处理,必须在该case语句处理完、下一个case语句前加上明确的注释。
  4. 注释格式尽量统一,建议使用“/*……*/”。
  5. 注释与对应代码采用同样缩进。

6 命名规则

6.1 命名规则总则

  1. 标识符(内部的和外部的)的有效字符不能多于31 
  2. 程序中不要出现仅靠大小写区分的相似变量名;局部变量不应与全局变量标识符使用相同的名称。

eg

uint8 test;

{

uint8  test;      /*定义了两个test  */    

       test  =  3;      /*这将产生混淆    */     

}

  1. 具有静态存储期的对象或函数标识符不能重用。

eg

              /* 定义了一个静态文件域变量test1 */

          static uint8 test1; 

        

uint8  test1;    /**在另一个文件又定义了一个具有外部链接的文件域变量test1*/

  1. 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。

eg

     好的命名方法:

          uint16   DebugMessage;

          uint8  ErrNum;

    不好的命名方法:

          uint16   dbmesg;

          uint8   en;

  1.  常见通用的单词缩写尽量统一,只能使用英语。

说明:对于特定的项目要使用的专有缩写应该注明或者做统一说明。

  1. 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
  2. 标示符尽量避免使用数字编号,除非逻辑上需要。

eg

         #define   DEBUG_0_MSG

         #define   DEBUG_1_MSG

应改为更有意义的定义:

#define   DEBUG_WARN_MSG

#define   DEBUG_ERR_MSG

  1. 不要使用单个字符(如i, j, k…)作为标示符,但i, j, k作局部循环变量是允许的。
  2. 不单独使用小写字母“l”或大写字母“O”作为变量名。

6.2 变量命名规则

  1. 全局变量添加G _前缀,全局静态变量添加” S_ ”,局部静态变量添加”s_”前缀,局部变量无前缀。使用一致的小写类型指示符作为前缀来区分变量类型,如下表。全局变量格式:前缀_变量类型_模块名称_变量名称,变量名称部分太长可用“_”分开。表示模块的缩写词全部大写,变量类型简称见表1

eg                                        

      s_s16_EEPROM_ErrorNum;

      S_u8_InSignal_PWMFlag;

      G_s16_OS_TimerOneStartReadyFlag_receive

表 1 变量类型的简称

原始类型

定义类型

解释

缩写

signed char

sint8

有符号字符型8 bit

s8

unsigned char

uint8

无符号字符型8 bit

u8

signed short

sint16

有符号短整型16 bit

s16

unsigned short

uint16

无符号短整型16 bit

u16

signed int

sint32

有符号整型32 bit

s32

unsigned int

uint32

无符号整型32 bit

u32

float

float32

浮点型32 bit

f32

double

float64

双精度 64bit

f64

unsigned long

uint32

无符号长整型32 bit

ult32

unsigned long

uint64

无符号长整型64 bit

ult64

signed long

int32

有符号长整型32 bit

slt32

signed long

int64

有符号长整型64 bit

slt64

unsigned char

boolean

布尔类型TRUE/FALSE

bool

  1. 局部变量格式:变量类型_变量名称,变量名称部分若太长可用“_”分开。大小写规则与全局变量相同。

eg

                uint8  u8_ InBusCANFlag_return 

  1. 定义指针变量*紧挨变量名,全局指针变量使用大写P前缀”P_”,局部指针变量使用小写p前缀”p _”

eg

uint32  *P_MsgAddress;              /*全局变量*/

uint8  *p_msg;                                  /*局部变量*/

  1. 常量(#define定义的常量、枚举、const定义的常量)的定义使用全大写字母,格式:模块名_名称,名称部分太长可用“_”分开。

eg

      #define    MATH_PI  3.14

const double MATH_PI = 3.14;

enum      weekday{ SUN,MON,TUE,WED,THU,FRI,SAT }; 

  1. 常数宏定义时,十六进制数的表示方法为0xFF

说明:前面0x中的x小写,数据中的”A-F”大写。

6.3文件及函数命名

  1. 文件名应为“模块+功能”,首字母大写分开组合单词,若有必要可使用“_”分隔符,模块名简称需大写。

需要完整文档点击如下链接下载:

百度MCU嵌入式C代码编码规范资源-****文库