嵌入式C语言代码规范

2023-05-16

C语言代码规范

参考安富莱C语言编码规范

1.文件与目录

1、文件及目录的命名规定可用的字符集是[A-Z;a-z;0-9;._-]。

2、源文件名后缀用小写字母 .c 和.h。

3、文件的命名要准确清晰地表达其内容,同时文件名应该精练,防止文件名过长而造成使用不便。在文件名中可以适当地使用缩写。
以下提供两种命名方式以供参考:

(1)各程序模块的文件命名开头 2 个消协字母代表本模块的功能:
如:主控程序为 mpMain.c,mpDisp.c …
(2)不写模块功能标识:
如:主控程序为 Main.c,Disp.c …

4、一个软件包或一个逻辑组件的所有头文件和源文件建议放在一个单独的目录下,这样有利于查找并使用相关的文件,有利于简化一些编译工具的设置。

5、对于整个项目需要的公共头文件,应存放在一个单独的目录下(例如:myProject/include)下,可避免其他编写人引用时目录太过分散的问题。

6、对于源码文件中的段落安排,我们建议按如下的顺序排列:

a. 文件头注释
b. 防止重复引用头文件的设置
c. #include 部分
d. #define 部分
e. enum 常量声明
f. 类型声明和定义,包括 struct、union、typedef 等
g. 全局变量声明
h. 文件级变量声明
i. 全局或文件级函数声明
j. 函数实现。按函数声明的顺序排列
k. 文件尾注释

7、在引用头文件时,使用 <> 来引用预定义或者特定目录的头文件,使用 “” 来引用当前目录或者路径相对于当前目录的头文件。

1 #include <file.h>
执行这条指令时,它会在系统目录中去查找 file.h 文件。在此条码命令中,不会去当前路径和附加路径中查找文件。

2 #include "file.h"
执行这条指令时,它首先会搜索附加路径,如果没有则会搜索系统路径,如果还没有则会去搜索当前路径。

8、为了防止头文件被重复引用,应当用 ifndef/define/endif 结构产生预处理块。

#ifndef __DISP_H /* 文件名前名加两个下划线“__”,后面加 “_H”
#define __DISP_H
...
...
#endif /* disp.h*/

9、头文件中只存放“声明”而不存放“定义”,通过这种方式可以避免重复定义。如果其它模块需要引用全局变量 g_temp, 只需要在文件开头包含 disp.h

#ifndef __DISP_H /* 文件名前名加两个下划线“__”,后面加 “_H”
#define __DISP_H
/** 全局变量声明 */
extern uint32_t g_temp;
#endif /* disp.h*/
#include "disp.h"
/** 全局变量定义 */
uint32_t g_temp = 0;

2.排版

1、程序块要采用缩进风格编写,缩进的空格数为 4 个。尽量用空格,在不同环境下Tab键代表的空格可能不同,导致排版混乱。
在这里插入图片描述
在这里插入图片描述

2、相对独立的程序块之间、变量说明之后必须加空行增加可读性、变量最好在使用时在定义。

3、当一行太长时,可以按操作符处划分新行,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

ANO_DT_send_int16((short)(sin(data1/180.0f * PI) * 100),
                  (short)(sin(data2/180.0f * PI) * 100),
                  (short)(sin(data3/180.0f * PI) * 100),
                  (short)(sin(data4/180.0f * PI) * 100), 
                  (short)(sin(data5/180.0f * PI) * 100), 
                   0, 
                   0,
                   0);  

4、不允许把多个短语句写在一行中,即一行只写一条语句。

5、程序块的分界符(如大括号‘{’和‘}’ )应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及 if、for、do、while、switch、case 语句中的程序都要采用如上的缩进方式。对于与规则不一致的现存代码,应优先保证同一模块中的风格一致性。

for (...) { <---- 不规范的写法

... /* program code */

}

for (...)

{ <---- 规范的写法

... /* program code */

}

if (...)

{ <---- 不规范的写法

... /* program code */

}

if (...)

{ <---- 规范的写法

... /* program code */

}

6、在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。
说明:采用这种松散方式编写代码的目的是使代码更加清晰。
由于留空格所产生的清晰性是相对的,所以,在已经非常清晰的语句中没有必要再留空格,如果语句已足够清晰则括号内侧(即左括号后面和右括号前面)不需要加空格,多重括号间不必加空格,因为在 C语言中括号已经是最清晰的标志了。
在长语句中,如果需要加的空格非常多,那么应该保持整体清晰,而在局部不加空格。给操作符留空格时不要连续留两个以上空格。

示例:
(1)逗号、分号只在后面加空格。

int_32 a, b, c;

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

if (current_time >= MAX_TIME_VALUE)

a = b + c;

a *= 2;

a = b ^ 2;

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

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

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

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

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

(4)"->"、"."前后不加空格。

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

3.注释

1、一般情况下,源程序有效注释量必须在 20%以上。
说明:注释的原则是有助于对程序的阅读理解,在该加的地方都加,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。

2、在文件的开始部分,应该给出关于文件版权、内容简介、修改历史等项目的说明。
具体的格式请参见如下的说明。在创建代码和每次更新代码时,都必须在文件的历史记录中标注版本号、日期、作者、更改说明等项目。

/*!
  * @file     LQ_ADC.c
  *
  * @brief    ADC驱动文件
  *
  * @company  北京龙邱智能科技
  *
  * @author   LQ-005
  *
  * @note     无
  *
  * @version  V1.1  2019/12/06 优化注释 Doxygen
  *
  * @par URL  http://shop36265907.taobao.com
  *           http://www.lqist.cn
  *
  * @date     2019/10/18 星期五
  */ 

3、对于函数,在函数实现之前,应该给出和函数的实现相关的足够而精练的注释信息。内容包括本函数功能介绍,调用的变量、常量说明,形参说明,特别是全局、全程或静态变量(慎用静态变量),要求对其初值,调用后的预期值作详细的阐述。具体的书写格式和包含的各项内容请参见如下的例子。

/*!
 * @brief    ADC通道初始化
 *
 * @param    channel   :  ADC通道 LQ_ADC.h中的一个枚举体  
 * @param    bit       :  ADC通道精度 LQ_ADC.h中的一个枚举体  
 *
 * @return   无
 *
 * @note     读取ADC之前一定要调用该函数对ADC通道进行初始化
 *
 * @see      ADC_InitConfig(ADC0CH0_P0_10, ADC_12bit);  //初始化ADC通道0 P0_10
 *
 * @date     2019/10/21 星期一
 */
void ADC_InitConfig(ADCn_Ch channel, ADC_nbit bit)

对于宏定

/*! CTIMER 最大占空比 可自行修改 */
#define  CMTER_PWM_MAX    10000

结构体、枚举体注释

/** 
  * @brief CTIMER模块 脉冲计数通道
  * @note  CTIMER 模块 脉冲捕获通道 
  * @note  CTIMER 模块的输入管脚并不是直接CTIMER连接的  而是通过INPUTMUX模块连接的
  * @note  CTIMER计数器 ---  INPUTMUX输入多路复用模块17路通道  ----  芯片外部管脚
  * @note  简单说 就是CTIMER的捕获通道每个都可以与 INPUTMUX模块的17路通道相链接
  * @note  INPUTMUX模块的17路通道相链接的管脚如下
  */ 
typedef enum
{
    CTInput0_P0_1  = 0x0000 + 1,  CTInput0_P0_13 = 0x0000 + 2,     /*!< INPUTMUX—CTIMER 输入通道0管脚  */  
    CTInput1_P0_14 = 0x0100 + 1,  CTInput1_P0_2  = 0x0100 + 2,     /*!< INPUTMUX—CTIMER 输入通道1管脚  */  
    CTInput2_P1_0  = 0x0200 + 1,  CTInput2_P1_28 = 0x0200 + 2,     /*!< INPUTMUX—CTIMER 输入通道2管脚  */  
    CTInput3_P1_1  = 0x0300 + 1,  CTInput3_P1_26 = 0x0300 + 2,     /*!< INPUTMUX—CTIMER 输入通道3管脚  */  
    CTInput4_P1_9  = 0x0400 + 1,  CTInput4_P0_16 = 0x0400 + 2,     /*!< INPUTMUX—CTIMER 输入通道4管脚  */  
    CTInput5_P1_11 = 0x0500 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道5管脚  */  
    CTInput6_P1_13 = 0x0600 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道6管脚  */  
    CTInput7_P1_15 = 0x0700 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道7管脚  */  
    CTInput8_P0_24 = 0x0800 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道8管脚  */  
    CTInput9_P0_25 = 0x0900 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道9管脚  */  
    CTInput10_P0_10= 0x0A00 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道10管脚  */  
    CTInput11_P0_28= 0x0B00 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道11管脚  */  
    CTInput12_P0_4 = 0x0C00 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道12管脚  */  
    CTInput13_P0_6 = 0x0D00 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道13管脚  */  
    CTInput14_P1_20= 0x0E00 + 1,  CTInput14_P0_26= 0x0E00 + 2,     /*!< INPUTMUX—CTIMER 输入通道14管脚  */  
    CTInput15_P0_20= 0x0F00 + 1,  CTInput15_P0_22= 0x0F00 + 2,     /*!< INPUTMUX—CTIMER 输入通道15管脚  */  
    CTInput16_P0_15= 0x1000 + 1,                                   /*!< INPUTMUX—CTIMER 输入通道16管脚  */  
    
} CTIMER_InputChannel_t;

全局变量

/** DMA link传输描述符 */
extern dma_descriptor_t s_dma_descriptor_table0[];

注释应严格按以上格式进行注释,方便日后使用Doxygen生成帮助API文档
在这里插入图片描述

4、边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

5、注释的内容要清楚、明了,含义准确,防止注释二义性。
说明:错误的注释不但无益反而有害。注释主要阐述代码做了什么(What),或者如果有必要的话,阐述为什么要这么做(Why),注释并不是用来阐述它究竟是如何实现算法(How)的。

6、普通注释格式尽量统一,建议使用“/* …… */”注释在代码上方, C++注释“//”并不被所有 C 编译器支持。

7、注释应考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,建议多使用中文,除非能非常流利准确的用英文表达。

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

9、命名中若使用特殊约定或缩写,则要有注释说明。

10、自己特有的命名风格,要自始至终保持一致,不可来回变化。

11、 对于变量命名,禁止取单个字符(如 i、j、k…),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但 i、j、k 作局部循环变量是允许的。

12、 除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。

13、除非必要,不要用数字或较奇怪的字符来定义标识符。

4.可读性

1、注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。

word = (high << 8) | low;
if ((a | b) && (a & c))
if ((a | b) < (c & d))

2、避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。

if (Trunk[index].trunk_state == 0) <---- 不规范的写法,应使用有意义的标识
{
	Trunk[index].trunk_state = 1; <---- 不规范的写法,应使用有意义的标识
... /* program code */

}
enum trunk_state_e
{
	TRUNK_IDLE = 0,

	TRUNK_BUSY = 1
};

if (Trunk[index].trunk_state == TRUNK_IDLE)
{
	Trunk[index].trunk_state = TRUNK_BUSY;
... /* program code */

}

3、不要使用难懂的技巧性很高的语句,除非证明改语句是性能瓶颈。

4、为了方便书写及记忆,变量类型采用如下重定义:

typedef unsigned char uint8_t;

typedef unsigned short uint16_t;

typedef unsigned long int uint32_t;

typedef signed char int8_t;

typedef signed short int16_t;

typedef signed long int int32_t;

#define __IO volatile

5、对于一些常见类型的变量,应在其名字前标注表示其类型的前缀。前缀用小写字母表示。前缀的使用请参照下列表格中说明。
在这里插入图片描述

6、变量作用域的前缀
为了清晰的标识变量的作用域,减少发生命名冲突,应该在变量类型前缀之前再加上表示变量作用域的前缀,并在变量类型前缀和变量作用域前缀之间用下划线‘-’隔开。
具体的规则如下:
(1)对于全局变量(global variable),在其名称前加“g”和变量类型符号前缀。

/** 全局变量 */
uint32_t g_ulParaWord;

/** 全局变量 */
uint8_t g_ucByte;

(2)对于静态变量(static variable),在其名称前加“s”和变量类型符号前缀。

/** 静态变量 */
static uint32_t s_ulParaWord;

/** 静态变量 */
static uint8_t s_ucByte;

(3)函数内部等局部变量前不加作用域前缀。

(4)对于常量,当可能发生作用域和名字冲突问题时,以上几条规则对于常量同样适用。注意,虽然常量名的核心部分全部大写,但此时常量的前缀仍然用小写字母,以保持前缀的一致性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式C语言代码规范 的相关文章

随机推荐