编码风格之(1)C语言规范(锐捷风格)

Author:Once Day Date:2023年12月27日

配套的clang-format配置可参考文档:

1.文件
1.1 文件名必须为小写,以模块名开始,可用字母、数字、下划线。
1.2 文件内部组织如下:
  1. 程序文件摘要,包括版权说明、作者信息、功能概括和版本历史信息等。

  2. 头文件包含,<x.h>在前,”x.h”在后。

  3. 顺序定义常数宏,定义函数宏,typedef、struct和enums。

  4. 静态数据声明和定义。

  5. 函数,应按它们的逻辑关系而非调用顺序来组织。

1.3 单个文件所包含的行数应控制在1 KLOC以内为宜,最长不超过2 KLOC。
1.4 不在头文件中定义变量及函数,inline类型的函数除外
1.5 头文件采用以下整体布局
/* sys/types.h */ 
#ifndef _SYS_TYPES_H_  ß 注意宏定义中包含分路径名(主目录下面不需要)
#define _SYS_TYPES_H_
.../* body of types.h */
#endif /* _SYS_TYPES_H_ */ ß 强制加此注释
2.注释
2.1 注释写”发生了什么“,应直接解释代码,避免使用间接引用说明。
/* ß 注意块注释中‘*’的位置,注意‘*’的对齐
 * 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 程序文件的开头的注释
/*
 * Copyright(C) 2005 Ruijie Network. All rights reserved.
 */ 2005是指本文件的履历,如2009年再修改本文件,则改为2005-092005, 20092005-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行的复合语句,应在结束行的末尾加注释标记

如果是多重嵌套的大跨度复合语句,末尾注释中需要更多信息以便区分对应重数。

while (len--) {
    sum += *data;
    sum <<= 1;
    ...
} /* End of while */ ß 超过30行的复合语句强制加此注释
3. 版式
3.1  般性的内容分段使用一个空白行,文件以‘\n’结束
#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风格”
control {
    statement;
    statement;
}
3.9 Switch使用规则

注意case与switch在同一列

switch (expr) {
case ABC: ß 这种情况不需加注释
case DEF:
    statement;
    break;
case UVW:
    statement;
    /* FALLTHROUGH */ ß 强制加注释
case XYZ:
    statement;
    break;
default:
    break;
}
3.10 没有循环体的for、while
while (*dest++ = *src++) {ß一定要用大括号
    (void)0;
}
3.11 不允许省略括号
3.12 类型、数值、注释上下行之间应对齐于制表位。
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 缩写除有约定俗成的形式外,采用以下步骤进行缩略:
  1. 删除元音字符
  2. 删除连续重复字符,只保留一个
  3. 保留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合为一体,而不定义结构名称。
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 外部使用的每个函数应在定义时进行完备注释。
/** ß 注意此处是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)
7.5 函数内局部变量的定义与初始化赋值应分开,初始化赋值靠近变量初次使用的地方.
7.6 不对局部变量做无意义的初始化。
8.杂项
8.1 源程序在编译时应消除所有编译警告
8.2 禁止采用C++语法
01-01 18:52