二、排版格式与注释
排版是为了在编写代码的时候按照“一定的规矩”来编写,主要目的是为了是代码清晰、 易于阅读。注释顾名思义就为注释自己的代码,以方便他人阅读,尤其是尤其维护人员。优美的排版和言简意赅的注释可以提高阅读者的阅读效率,所以在编写代码之前一定要确定好自己打算采用的排版方式和注释方式。
1、排版格式
1.1、代码缩进
(1)代码缩进要使用制表符,也就是 TAB 键,一般情况下一个 TAB 为 4 个字符,但是也有 8 个甚至更多的情况,为了统一规范,我们统一规定:TAB 键为 4 个字符。
(2)另外,我们规定:TAB 键缩进用空格替代。因为不同的编译器,对 TAB 键的长度是不完全一样的,所以如果 TAB 键不用空格替代,在不同编译器/阅读软件打开的时候,会有很大的不同,有可能导致看起来异常难看。最简单的方式你可以用 txt 打开.c/.h 来查看代码,就会发现和编译器有所不同,如果你用空格键替 TAB 缩进字符,则无论用什么软件打开看起来都是差异不大的。
(3)对于 MDK 编译器,我们可以通过在: 设置→ Configuration → Edit → C/C++ Files 里面勾选Insert spaces for tabs 来设置每次按 TAB 都是用空格进行缩进填充。 另外,我们还可以通过在: 设置→ Configuration → Edit → General Edit Settings 里面勾选 View White Spaces 来查看所有的空格(用‘.’替代空格),勾选之后如下图所示:
可以看到,上图中所有的代码都用空格缩进了(’.’),不过有一处地方,TAB 键还是没用空格替代,就是 LED2(1);这行代码之后的注释缩进,是一个 →,表示这个 TAB 键不是用空格替代的,这样在不同的编译器/软件打开这段代码的时候,这个非空格替代 TAB 键会有不同的长度,如果不是 4 个字符,就会和其他位置产生差异,因而无法对齐。
(4)所以,我们规定:无论是代码缩进,还是注释缩进,统一用 TAB 键对齐,且 TAB 键设置为用 4 个空格替代缩进。
1.2、代码行相关规范
(1)每一行的代码长度限制在 80 列。如果大于 80 列的话就要分成多行编写,并且在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要适当进行缩进与上一行代码对齐,如下所示:
perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN+ STAT_SIZE_PER_FRAM * sizeof( _UL );act_task_table[frame_id * STAT_TASK_CHECK_NUMBER + index].occupied= stat_poi[index].occupied;if ((taskno < max_act_task_number)&& (n7stat_stat_item_valid (stat_item)))
{... /* program code */
}report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)&& (n7stat_stat_item_valid (stat_item))&& (act_task_table[taskno].result_data != 0));
n7stat_str_compare((BYTE *) & stat_object,(BYTE *) & (act_task_table[taskno].stat_object),sizeof (_STAT_OBJECT));
(2)相对独立的程序块之间、变量定义语句写完之后,必须加空行。函数之间,必须加空行。
不规范的写法:
void funa(...)
{if (!valid_ni(ni)){... /* program code */}repssn_ind = ssn_data[index].repssn_index;repssn_ni = ssn_data[index].ni;while(x == 0){... /* program code */}
}
void funb(...)
{... /* program code */
}应改为:
void funa(...)
{if (!valid_ni(ni)){... /* program code */}repssn_ind = ssn_data[index].repssn_index;repssn_ni = ssn_data[index].ni;while(x == 0){... /* program code */}
}void funb(...)
{... /* program code */
}
不规范的写法:
a = x+y; b = x-y;应改为:
a=x+y;
b=x-y;
(4)不要在一行里面放置多个赋值语句。
不规范写法:
a = b = 0;应改为:
a = 0;
b = 0;
(5)xxif、for、do、while、case、swich、default 等语句单独占用一行。且 if、for、do、while 等语句的执行语句部分无论多少都要加括号{},当且仅当 while 后为空,可以不加{}。
不规范的写法:
if (p_gpiox->IDR & pinx) return 1; /* pinx 的状态为 1 */
else return 0; /* pinx 的状态为 0 */应改为:
if (p_gpiox->IDR & pinx)
{return 1; /* pinx 的状态为 1 */
}
else
{return 0; /* pinx 的状态为 0 */
}
(6)对齐全部用空格,或者说用空格填充的 TAB 键。 用空格填充 TAB 是为了避免垮编译器/不同软件打开时的差异导致代码排版不一的情况。
1.3、括号与空格
1.3.1、括号
(1)代码中用到大括号“{”和“}”的地方,对于单片机开发来说,左括号“{”一律新起一行,且位于程序块开始的同一列。对于 Linux 开发,则允许“{”放在行位,以单片机开发为例:
不规范的写法:
for (...) {... /* program code */
}if (...)
{... /* program code */
} else {... /* program code */
}void example_fun( void ){... /* program code */}应改为:
for (...)
{... /* program code */
}if (...)
{... /* program code */
}
else
{... /* program code */
}void example_fun( void )
{... /* program code */
}
(2)当 while 语句没有代码的时候,可以不用加“{}”,但是,只要 while 有哪怕 1 条语句,就必须加“{}”如下所示:
不规范的写法:
while (((RCC->CR & (1 << 17)) == 0) && (retry < 0X7FFF)){ retry++; }应改为:
while (((RCC->CR & (1 << 17)) == 0) && (retry < 0X7FFF))
{retry++;
}while 后面没有代码的时候,可以不要“{}”
while ((QUADSPI->SR & (1 << 1)) == 0); /* 等待指令发送完成 */
1.3.2、空格
(1)在一些关键字后面要添加空格,如:
if、swich、case、for、do、while
但是不要在 sizeof、alignof 或者__attribute__这些关键字后面添加空格,因为这些大多数后面都会跟着小括号,因此看起来像个函数,如:
s = sizeof(struct file);
(2)如果要定义指针类型,或者函数返回指针类型时,“*”应该放到靠近变量名或者函数名的一侧,而不是类型名,如:
char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);
(3)二元或者三元操作符两侧都要加一个空格,例如下面所示操作符:
= + - < > * / % | & ^ <= >= == != ? :
(4)一元操作符后不要加空格,如
& * + - ~ ! sizeof typeof alignof __attribute__ defined
(5)后缀自加或者自减的一元操作符前后都不加空格,如:
++ --
(6)“.”和“->”这两个结构体成员操作符前后不加空格。
(7)逗号、分号只在后面添加空格,如:
int a, b, c;
(8)注释符“/*”和“*/”与注释内容之间要添加一个空格。
以上 8 点举例如下:
不规范的写法:
void test_error(datax* p,int num,char baseval)
{int x1 ,x2;int t=0;x1=32;x2=23;for(t=0;t<=num;t ++ ) /*循环赋值*/{datax -> buf[ t ]=x1*(x2+t)+baseval;}
}应改为:
void test_error(datax *p, int num, char baseval)
{int x1, x2;int t = 0;x1 = 32; x2 = 23;for (t = 0; t <= num; t++) /* 循环赋值 */{datax->buf[t] = x1 * (x2 + t) + baseval;}
}
2、注释
2.1、注释风格
注释可以让别人一看你的代码就明白其中的含义和用法, 但是不要过度注释, 不要在注释里解释代码是如何运行的,一般你的注释应该告诉别人代码做了什么,而不是怎么做的,即结果,而非过程!
当前代码注释统一采用 doxygen 风格,并统一使用如下注释风格:
具体代码的注释:
/* ……… */
函数/文件说明注释格式:
/**
* ………
* ………
* ………
*/
放弃使用:
// ………
// ………
多行注释,在每一行的开始处都应该放置符号“*”,并且所有的行的“*”要对齐在一列上。注意,当且仅当屏蔽掉部分功能代码(以便后续调试/修改使用)时,可以使用 // 注释。如:
代码段 1:
if (timeout == 0)
{//printf("r fifo time out\r\n");SDMMC1->ICR = 0X1FE00FFF; /* 清除所有标记 */sys_intx_enable(); /* 开启总中断 */return SD_DATA_TIMEOUT;
}代码段 2:
while (res) /* 读出错 */
{if (res != 2)sd_init(); /* 重新初始化 SD 卡 */res = sd_read_disk(buff, sector, count);//printf("sd rd error:%d\r\n", res);
}
以上代码使用 // 屏蔽 printf, 因为这是一行有效代码,可以辅助分析 SD 卡问题,只是这里屏蔽掉,不使用而已。当 SD 卡通信出现问题的时候,我们可以通过取消这个屏蔽,从而通过 printf 输出辅助信息,方便查找问题。
2.2、文件信息注释
2.3、函数的注释
2.4、代码注释
三、标识符命名
四、函数
五、变量
六、宏和常量