编码风格之(1)C语言规范(锐捷风格)
Author:Once Day Date:2023年12月27日
配套的clang-format配置可参考文档:
1.文件
1.1 文件名必须为小写,以模块名开始,可用字母、数字、下划线。
1.2 文件内部组织如下:
-
程序文件摘要,包括版权说明、作者信息、功能概括和版本历史信息等。
-
头文件包含,<x.h>在前,"x.h"在后。
-
顺序定义常数宏,定义函数宏,typedef、struct和enums。
-
静态数据声明和定义。
-
函数,应按它们的逻辑关系而非调用顺序来组织。
1.3 单个文件所包含的行数应控制在1 KLOC以内为宜,最长不超过2 KLOC。
1.4 不在头文件中定义变量及函数,inline类型的函数除外
1.5 头文件采用以下整体布局
c
/* sys/types.h */
#ifndef _SYS_TYPES_H_ ß 注意宏定义中包含分路径名(主目录下面不需要)
#define _SYS_TYPES_H_
.../* body of types.h */
#endif /* _SYS_TYPES_H_ */ ß 强制加此注释
2.注释
2.1 注释写"发生了什么",应直接解释代码,避免使用间接引用说明。
c
/* ß 注意块注释中'*'的位置,注意'*'的对齐
* Here is a block comment
* The comment text should be spaced over uniformly.
* The opening slash-star and closing slash-star should are
* alone on a line.
*/
2.2 对于一行代码的注释可放在前一行及本行上,不允许放在下一行。
2.3 注解统一采用/* comments */形式,不得使用'//'风格的注释。
2.4 程序文件的开头的注释
c
/*
* Copyright(C) 2005 Ruijie Network. All rights reserved.
*/ 2005是指本文件的履历,如2009年再修改本文件,则改为2005-09或2005, 2009或2005-2009
/*
* mktime.c ß 文件名
* Original Author: staff1@ruijie.com.cn, 2005-8-1 ß 最初作者,创建日期
* ß 以下是功能的简要说明
* xxxxxxxxxxxxxxxxxxxxxxxx
*
* History ß 记录1.0版以后的重大修改,1.0版不需要历史记录
* v1.2 stsff3@ruijie.com.cn 2005-10-1 ß 倒序记录
* XXXXXYYYYY ß 本次修改的简要说明
* v1.1 stsff2@ruijie.com.cn 2005-9-1
* XXXXXYYYYY
*/
2.5 对于跨度较大、超过30行的复合语句,应在结束行的末尾加注释标记
如果是多重嵌套的大跨度复合语句,末尾注释中需要更多信息以便区分对应重数。
c
while (len--) {
sum += *data;
sum <<= 1;
...
} /* End of while */ ß 超过30行的复合语句强制加此注释
3. 版式
3.1 般性的内容分段使用一个空白行,文件以'\n'结束
c
#include <stdio.h>
ß 头文件包含后,与后续语句之间有一空行
#define HELLO_CNT 3
ß 文件全部宏定义,与后续语句之间有一空行
int main(void)
{
int i;
ß 函数内声明与语句之间有一空行
for (i = 0 ; i < HELLO_CNT; i++) {
printf("hello world.\n");
}
ß 函数内逻辑块之间有一空行
return 0;
}
3.2 每代码行不得多于100列,代码中连续空白行数量不得超过1行。
3.3 当代码行较长需要断行时,应从最低优先级运算符处断开,将运算符置于该行末尾,并加以适当的缩进。
3.4 代码的缩进应使用空格(SPACE),而非制表符(TAB),缩进以4字符为单位。
3.5 条件编译不缩进,均从第一列开始,不考虑它们的层次关系。而语句行则仍沿用其应有的缩进,就象条件编译语句不存在一样。
3.6 空格出现在以下地方:
-
关键字与其后面表达式之间有空格。
-
单目操作符不应与它们的操作数分开。
-
除'.'和'->'外,其它双目操作符应与它们的操作数用空格隔开。
3.7 每行只能包括一个语句
3.8 if-else、for、while和do-while复合体的大括号使用采用"K&R风格"
c
control {
statement;
statement;
}
3.9 Switch使用规则
注意case与switch在同一列
c
switch (expr) {
case ABC: ß 这种情况不需加注释
case DEF:
statement;
break;
case UVW:
statement;
/* FALLTHROUGH */ ß 强制加注释
case XYZ:
statement;
break;
default:
break;
}
3.10 没有循环体的for、while
c
while (*dest++ = *src++) {ß一定要用大括号
(void)0;
}
3.11 不允许省略括号
3.12 类型、数值、注释上下行之间应对齐于制表位。
c
struct boat {
int wllength; /* water line length in meters */
int type; /* see below */
long sailarea; /* sail area in square mm */ ß 注释的对齐
};
/* defines for boat.type */
#define KETCH 1 ß 常数的对齐
#define YAWL 2
#define SLOOP 3
#define SQRIG 4
#define MOTOR 5
4.命名
4.1 不能以下划线作为名字的第一个或最后一个字符
4.2 宏定义大写,枚举常数大写,其他都小写。
4.3 以typedef形式定义的名字应加上'_t'后缀。
4.4 函数命名形式:模块名_动词_宾语,函数名应准确描述函数的功能
4.5 命名规则细分:
-
静态局部函数无需使用模块名前缀来标识其所属的模块。
-
函数名原则上不超过30个字符,超过30个字符的函数占该文件全部函数比例不超过10%。
4.6 全局变量命名形式:g_模块名_说明性的名字
4.7 全局变量不应长于21个字符;局部变量不应长于10个字符
4.8 缩写除有约定俗成的形式外,采用以下步骤进行缩略:
- 删除元音字符
- 删除连续重复字符,只保留一个
- 保留3~7个字符
5. 常数和宏定义
5.1 避免在程序中直接出现常数(即magic number),使用超过一次的常数应以宏定义或变量的常数形式来进行替代;仅使用一次的常数可使用注释说明。
5.2 宏定义函数的参数及宏定义整体应加括号,以免宏展开时由于优先级组合不同于预期而产生意外结果
6. 变量
6.1 有初值的变量都应该显式地初始化,但那些在C语言中有明确约定的除外。
6.2 结构(struct)或多维数组的初始化,应该有足够的括号分清层次,而不是让编译器来进行区分,但{0}形式的除外。
6.3 文件中的变量及函数应尽量用'static'来缩小其可见域。
6.4 采用typedef定义struct类型时,它们应该紧邻的,并且typedef与struct使用相同的前缀名字,或者将结构声明与typedef合为一体,而不定义结构名称。
c
typedef struct splodge {
int sp_count;
char *sp_name, *sp_alias;
} splodge_t;
或者:
typedef struct { ß struct没有名字
int sp_count
char *sp_name, *sp_alias;
} splodge_t;
6.5 程序中进行变量类型转换时,需要在语句中显式地指出意图中的转换。
6.6 不得在.c文件中直接以extern形式直接声明外部符号,这些符号应放在头件中说明。
7.函数
7.1 一个函数应控制在120行(不含空白与注释在内)以内,不符合本条需特别给出说明
7.2 当函数的声明与定义在不同的文件中时,函数声明采用简单行注释形式,定义采用完备注释形式。
7.3 仅在本文件内部使用的static函数可以不加注释。
7.4 外部使用的每个函数应在定义时进行完备注释。
c
/** ß 注意此处是2个'*',这种格式只用于外部接口说明
* call_usermodehelper - start a usermode application
* @path: pathname for the application ß 注意参数前有'@'
* @argv: null-terminated argument list
* @envp: null-terminated environment list
*
* Runs a user-space application. The application is started
* asynchronously. It runs as a child of keventd. It runs wite
* full root capabilities. keventd silently reaps the child when
* it exits.
*
* Must be called from process context. Returns zero on success,
* else a negative error code.
*/
int call_usermodehelper(char *path, char **argv, char **envp)