如何增强代码的可读性?嵌入式代码之编写规范
转载自:玩转单片机
嵌入式代码编码规范,用于规范自己的代码,增强可读性,非标准规范。最好能强制自己形成良好的编码风格,有利于开发大规模程序而不显得杂乱。参考STM32固件库编码风格和FreeRTOS编码风格。
一、工程文件组织结构
新建工程文件应包含以下全部或部分文件夹:
一个工程一定要包含一个main.c文件,只用来存放main函数。其余函数的定义应在相应的.c文件中,声明在相应的.h文件中。
So can you help to prepare our suggest contents display in the page (e.g. tools intro, feature, which videos, which documents etc) to us first? We will then further discuss with Infineon if anything need to change / add.
二、源文件
文件头,文件的简介
usrSrc:用户源文件,用来存放.c文件和其他的源文件。main.c应放在这里。
usrInc:用户头文件,用来存放.h文件。
usrDoc:用户说明文档,用来存放用户在开发过程中书写的文档,一般为.txt格式。例如Readme.txt,指令说明等。
Src:引用库的源文件。
Inc:应用库的头文件。
Lib:引用的库文件。
一个工程一定要包含一个main.c文件,只用来存放main函数。其余函数的定义应在相应的.c文件中,声明在相应的.h文件中。
So can you help to prepare our suggest contents display in the page (e.g. tools intro, feature, which videos, which documents etc) to us first? We will then further discuss with Infineon if anything need to change / add.
二、源文件
文件头,文件的简介
1 /*************************************************************************
2 * Copyright (c) 2018, Xiaodan
3 * All rights reserved.
4 *
5 * File name : USB_Ctrl.c
6 * Brief : USB API source code.
7 * Introduce the main function or content of this document briefly.
8 * Revision : 1.01
9 * Author : Xiaodan
10 * Date : 2018.11.06
11 * Update : Introduce the difference from previous version.
12 *************************************************************************/
1 必要的注释和说明
2
3 源文件应该只包含它自己的头文件,其他的头文件在他自己的头文件中包含。
4
5 源文件中只声明局部函数,全局函数在头文件中声明。
6
7 全局变量在相应的源文件中定义,在头文件中用extern声明。
8
9 类型定义在头文件中定义。
10
11 /* Includes -----------------------------------------------------------*/
12 /*only include it's own header file, the others header file included by USB_Ctrl.h*/
13 #include "USB_Ctrl.h"
14
15 /* Declaration --------------------------------------------------------*/
16 /*here are the local function declare, global fuction declare in header file.*/
17 void delay( uint32_t n);
18 /* Global variable ----------------------------------------------------*/
19 函数头
20
21 /******************************************************
22 * Brief : Delay n ms
23 * Parameter :
24 * n: the number of delay microsecond.
25 * Return : None.
26 *******************************************************/
27 void delay( uint32_t n)
28 {
29 for( i=0; i<110; i++)
30 ;
31 }
1 三、头文件
2
3
4
5 文件头,文件的简介
6
7 /*************************************************************************
8 - Copyright (c) 2018, Xiaodan
9 - All rights reserved.
10 -
11 - File name : USB_Ctrl.h
12 - Brief : The header file of USB_Ctrl.c.
13 - Revision : 1.01
14 - Author : Xiaodan
15 - Date : 2018.11.06
16 - Update : Introduce the difference from previous version.
17 *************************************************************************/
18 必要的注释和声明
19
20 /* Includes -----------------------------------------------------------*/
21 /* Define -------------------------------------------------------------*/
22 /* Typedef ------------------------------------------------------------*/
23 typedef unsigned int apiStatus; //api return code
24 /* Enume --------------------------------------------------------------*/
25 /* Extern -------------------------------------------------------------*/
26 /* Declaration --------------------------------------------------------*/
27 四、命名规则
28
29
30
31 参考FreeRTOS命名规则,MISRA C规范。
32
33 定义变量时尽量使用uint8_t 、uint16_t 、uint32_t等。头文件为stdint.h。
34
35
36
37 typedef signed char int8_t;
38 typedef short int16_t;
39 typedef int int32_t;
40 typedef long long int64_t;
41 typedef unsigned char uint8_t;
42 typedef unsigned short uint16_t;
43 typedef unsigned int uint32_t;
44 typedef unsigned long long uint64_t;
45 uint32_t类型的变量使用前缀ul,这里’u’表示’unsigned’,’l’表示’long’
46
47 uint16_t类型的变量使用前缀us,这里’u’表示’unsigned’,’s’表示’short’
48
49 uint8_t类型的变量使用前缀uc,这里’u’表示’unsigned’,’c’表示’char’
1 枚举类型变量使用前缀e
2
3 指针类型变量在类型基础上附加前缀p,比如指向uint16_t的指针变量前缀为pus
4
5 与MISRA指南一致,char类型变量仅被允许保存ASCII字符,前缀为c
6
7 与MISRA指南一致,char *类型变量仅允许指向ASCII字符串,前缀为pc
8
9 宏定义全部使用大写,两个单词之间用下划线隔开。
10
11 具有文件作用域的对象尽量声名为static的。
12
13 全局变量加前缀’g_’。整个工程都可以用的变量,不局限于文件作用域。
14
15 五、代码风格
16
17
18
19 缩进:缩进使用制表符,一个制表符等于4个空格。
20
21 注释:注释单行不超过80列,特殊情况除外。
22
23 布局:源代码应被设计成尽可能的易于查看和阅读。
24
25 下面的代码片中,第一部分展示文件布局,第二部分展示C代码设计格式。
26
27 /* #defines, 在合理的位置添加括号. */
28 #define A_DEFINITION ( 1 )
29
30 /*
31 * 随后是Static (文件内部的)函数原型,
32 * 如果注释有多行,参照本条注释风格---每一行都以’*’起始.
33 */
34 static void prvAFunction( uint32_t ulParameter );
35 /* 文件作用域变量(本文件内部使用),要在函数体定义之前. */
36 static BaseType_t xMyVariable.
37
38 /* 每一个函数的结束都有一行破折号,破折号与下面的第一个函数之间留一行空白。*/
39 /*-----------------------------------------------------------*/
40 void vAFunction( void )
41 {
42 /* 函数体在此定义,注意要用大括号括住 */
43 }
44 /*-----------------------------------------------------------*/
45 static UBaseType_t prvNextFunction( void )
46 {
47 /* 函数体在此定义. */
48 }
49
50 /*-----------------------------------------------------------*/
51
52 /*
53 * 函数名字总是占一行,包括返回类型。左括号之前没有空格左括号之后有一个空格,
54 * 每个参数后面有一个空格,参数的命名应该具有一定的描述性.
55 */
56 void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
57 {
58 /* 变量声明没有缩进. */
59 uint8_t ucByte;
60 /* 代码要对齐. 大括号占独自一行. */
61 for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
62 {
63 /* 这里再次缩进. */
64 }
65 }
66
67 /*
68 * for、while、do、if结构具有相似的模式。这些关键字和左括号之间没有空格。
69 * 左括号之后有一个空格,右括号前面也有一个空格,每个分号后面有一个空格。
70 * 每个运算符的前后各一个空格。使用圆括号明确运算符的优先级。不允许有0
71 * 以外的数字(魔鬼数)出现,必要时将这些数字换成能表示出数字含义的常量或
72 * 宏定义。
73 */
74 for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
75 {
76 }
77
78 while( ucByte < fileBUFFER_LENGTH )
79 {
80 }
81
82 /*
83 * 由于运算符优先级的复杂性,我们不能相信自己对运算符优先级时刻保持警惕
84 * 并能正确的使用,因此对于多个表达式运算时,使用括号明确优先级顺序
85 */
86 if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )
87 {
88 ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
89 }
90
91 /* 条件表达式也要像其它代码那样对齐。*/
92 #if( configUSE_TRACE_FACILITY == 1 )
93 {
94 /* 向TCB增加一个用于跟踪的计数器. */
95 pxNewTCB->uxTCBNumber = uxTaskNumber;
96 }
97 #endif
98
99 /*方括号前后各留一个空格*/
100 ucBuffer[ 0 ] = 0U;
101 ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;
102
103
104 六、编程思想
105
106
107
108 将特定功能的代码封装成函数。
109
110
111
112 七、C语言编程规则
113
114
115
116 参考MISRA
117
118
119
120 Rule1:不得使用三元操作符(? : )。
121
122 Rule2:不得残留被注释掉的废代码。
123
124 Rule3:所有标识符不超过31字符。
125
126 Rule4:不同名空间中的变量名不得相同。
127
128 例如:
129
130 typedef struct MyStruct {... } MyStruct; (违规)
131 struct Person {
132 char* name;
133 ...};
134 char name[32]; (违规)
135
136 Rule5: 不得使用char, int, float, double, long等基本类型,应该用stdint.h中定义的类型显示表示类型的大小,如uint16_t、int32_t等。
137
138 Rule6:禁止使用八进制数。(因为086U这样的常数很容易引起误解)。
139
140 Rule7:不得定义与外部作用域中某个标识符同名的对象,以避免遮盖外部作用域中的标识符。
141
142 Rule8:具有文件作用域的对象尽量声名为static的。
143
144 Rule9:自动对象(栈对象)使用前必须赋初值。
145
146 Rule10:操作符&&和||的右侧表达式不得具有副作用(side-effect)。
147
148 也就是说,象 if (x == 20 && ++y == 19)这样的表达式被禁止。
149
150
151
152 Rule11:不得对有符号数施加位操作,例如 1 << 4 将被禁止,必须写 1UL << 4。
153
154
155
156 Rule12:不得对有副作用的表达式施加sizeof操作符。
157
158 Rule13:除了循环控制语句,不得使用逗号表达式。
159
160 Rule14:不得显式判断浮点数的相等性和不等性。
161
162 Rule15:不得遗留“永远不会用到”的代码。
163
164 Rule16:除了switch语句,不得使用标号(label)。
165
166 Rule17:不得使用goto。
167
168 Rule18:不得使用continue。
169
170 Rule19:除了switch语句,不得使用break
171
172 Rule20:if, else if, else, while, do..while, for语句块必须使用{}括起。
173
174 Rule21:循环计数器的值不得在循环体内修改。
175
176 Rule22:禁止任何直接和间接的递归函数调用。
177
178 Rule23:不应该使用#undef。
179
180 Rule24:不得将宏作为参数传给宏函数。
181
182 Rule25:在一个宏定义中,#或##符号只能出现一次。
183
184 Rule26:禁止指针运算(代之以数组下标运算)。
185
186 Rule27:禁止超过两级的指针。
187
188 Rule28:禁止使用指向函数的非常量指针。
189
190 Rule29:禁止使用setjmp, longjmp。
191
192 Rule30:禁止使用atoi, atof, atol。(这个我很赞成,建议使用strtol, strtod等函数)
193
194 Rule31:禁止使用abort, exit, g