Linux内核编码规范 Linux kernel coding style

原文:https://www.kernel.org/doc/html/latest/process/coding-style.html#indentation

对于编码风格,着实体验了一把邯郸学步的感觉。起初的代码风格来源于教程,或者说没有代码风格。后来,就职于不同的公司,发现公司不同,代码编写规范各异。尤其是对于代码风格没有强制要求的公司,部门内部每个人都有自己的风格,见到优秀的学习一下,最后渐渐的发现自己的代码编写规范一直在变,到后来自己看自己之前写的代码都觉得别扭。所以查看了linux内核的编码规范,用于规范自己的代码风格。

1. 缩进(Indentation)
缩进使用"Tab"键,固定八个字符;

代码不要超过三层缩进;

switch语句中:

"switch"和"case"关键字左对齐;
分支指令一层缩进;
"break"关键字与分支指令对齐(不是与"case"关键字左对齐);
    switch (suffix) {
    case 'G':
    case 'g':
            mem <<= 30;
            break;
    case 'M':
    case 'm':
            mem <<= 20;
            break;
    case 'K':
    case 'k':
            mem <<= 10;
            fallthrough;
    default:
            break;
    }
单独一行里不要写多个语句,包括不要把多个赋值语句放在同一行;

内核的代码风格是十分简洁的,请尽量避免使用复杂的表达式;

除了在注释、文档和Kconfig中,不要使用空格作为缩进;

不要在行末留有空格

2. 换行(Breaking long lines and strings)
规范代码风格的目的是提高代码的可读性和维护性;

强烈推荐单行的宽度限制为八十列;

拆分超过八十列宽度的语句为多行:

如果超过八十列的部分可以提高可读性且不会隐藏信息时可以不拆分;
适用范围:宽度超过八十列的语句或有很长参数列表的函数头;
拆分出的子句长度应该比主句短;
拆分出的子句尽量靠右;
不要拆分用户可见的字符串(如"printk"的信息,否则会导致使用"grep"时找不到这些信息);
疑问:
在编辑器中怎么限制(提醒)单行的宽度为"八十列"?
"不会隐藏信息时"指什么?
"用户可见的字符串"指什么?
"用户可见的字符串"出现了"隐藏信息时"怎么处理?
3. 花括号和空格(Placing Braces and Spaces)
3.1. 花括号(大括号)
把左括号放在行末,右括号放在行首;

适用范围:非函数的语句块(if,switch,for,while,do);

函数的左括号应该放在行首;

右括号一般单独成一行,除非右括号之后有紧密结合的语句(如:do-while,if-else if-else等);

    //情况一:左括号放在行末,右括号放在行首
    if (x is true) {
            we do y
    }
 
    //情况二:左括号放在行末,右括号放在行首
    switch (action) {
    case KOBJ_ADD:
            return "add";
    case KOBJ_REMOVE:
            return "remove";
    case KOBJ_CHANGE:
            return "change";
    default:
            return NULL;
    }
 
    //情况三:函数的左括号应该放在行首
    int function(int x)
    {
            body of function
    }
 
    //情况四:右括号之后有紧密结合的语句
    do {
            body of do-loop
    } while (condition);
 
    //情况五:右括号之后有紧密结合的语句
    if (x == y) {
            ..
    } else if (x > y) {
            ...
    } else {
            ....
    }
在不降低可读性的前提下尽可能减少空行的数量;

单行语句可以完成任务时不要使用括号;
其他"case"有多行的情况下,所有"case"都要使用括号;
循环中超过一条语句需要使用括号;
    //情况一:单行语句可以完成任务时不要使用括号
    if (condition)
        action();
 
    //情况二:单行语句可以完成任务时不要使用括号
    if (condition)
        do_this();
    else
        do_that();
 
    //情况三:其他"case"有多行的情况下,所有"case"都要使用括号
    if (condition) {
            do_this();
            do_that();
    } else {
            otherwise();
    }
 
    //情况四:循环中超过一条语句需要使用括号
    while (condition) {
            if (test)
                    do_something();
    }
3.2. 空格
Linux内核风格习惯在一些关键字之后添加一个空格;

使用方式类似函数的关键字使用不带空格的一对小括号;

使用方式类似函数的关键字:sizeof, typeof, alignof, __attribute__等等;

需要添加空格的关键子:if, switch, case, for, do, while;

不要在小括号内部周围添加空格(左括号右边,右括号左边);

指针声明或函数返回为指针时,星号紧靠变量名或函数名,不是类型名;

一元操作符右边不添加空格;

自增自减一元操作符,后缀前不加空格,前缀后不加空格;

结构体成员操作符周围不加空格;

二元操作符左右添加空格;

三元操作符周围添加空格;

逗号操作符左边不添加空格,右边添加空格;

for语法中分号左边不添加空格,右边添加空格;

不要在行末添加空格(某些编辑器会在新的行首添加一些空格来表示缩进,但是想保留一行空行时,这些由空格表示的缩进可能没有删除,导致看起来像是在上一行行末添加了多余的空格);

    //情况一:需要添加空格的关键子
    //if, switch, case, for, do, while
 
    /情况二:使用方式类似函数的关键字使用不带空格的一对小括号
    //sizeof, typeof, alignof, __attribute__
    s = sizeof(struct file);
 
    //情况三:不要在小括号内部周围添加空格(左括号右边,右括号左边)
    s = sizeof( struct file );  /* 不建议采用的示例 */
 
    //情况四:针声明或函数返回为指针时,星号紧靠变量名或函数名,不是类型名
    char *linux_banner;
    unsigned long long memparse(char *ptr, char **retptr);
    char *match_strdup(substring_t *s);
 
    //情况五:一元操作符右边不添加空格
    //&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined
 
    //情况六:自增自减一元操作符,后缀前不加空格,前缀后不加空格;
    i++;
    i--;
    --i;
    ++i;
 
    //情况七:结构体成员操作符周围不加空格
    student.age;
    pstudent->age;
 
    //情况八:二元操作符左右添加空格
    //=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=
 
    //情况九:三元操作符周围添加空格
    //?  :
 
    //情况十:逗号操作符左边不添加空格,右边添加空格
    //arg1, arg2, arg3
 
    //情况十一:for语法中分号左边不添加空格,右边添加空格
    for (i = 0; i < Cnt; i++) {
4. 命名(Naming)
C语言是一种简洁的语言,建议用tmp代替VariableIsATemporaryCounter;

全局变量或全局函数可以使用混合大小写的描述性名字,使用count_active_users()代替cntusr();

编译器会检查函数类型,所以不建议写一个包含函数类型的名字(匈牙利命名法);

局部变量名应该简洁;

可以用i来定义一个计数器;
可以用tmp来定义任何类型的临时变量;
备注:
列举常用的命名情况和特例!
5. 定义类型(Typedefs)
代码vps_t a;中的vps_t降低了代码的可读性,struct virtual_container *a;更易于理解代码,是typedef的错误使用方式;

typedef仅有的用法:

隐藏对象,封装作用(内核不建议使用封装和成员函数的思想);

抽象整数类型,避免混淆它是int型还是long型;

典型应用:u8/u16/u32;
用"sparse"创建用于类型检查的新类型;

Link1
Link2
某些情况下使用与标准C99相同的新型

标准C99类型:uint32_t(一些人反对使用);
Linux特有类型:u8/u16/u32/u64;
两种类型不强制,但是选定一种之后就应该一直使用,不建议混用;
用户空间的类型安全

用户空间的某些结构体可能不能使用标准C99类型和Linux特有类型,而使用"__u32"这类的类型;
如果不满足typedef的五种用法,就不建议使用typedef;

指针或有可访问元素的结构体不应该使用"typedef";

6. 函数(Functions)
一个函数只做一件事;

函数长度建议一到两屏幕(ISO/ANSI的屏幕尺寸是80x24);

函数的最大长度与该函数的复杂性和缩进程度成反比;

如果由于"case"太多导致的函数过长是可以理解的;

使用一些有描述性名称的辅助函数;

函数的性能至关重要时,可以使用内联;

函数的局部变量的数量不应该超过5-10个(人类的大脑通常可以很容易地跟踪大约7件不同的事情);

用一个空行分隔函数;

如果函数被导出,导出宏应该紧跟在结束函数的大括号后面

    int system_is_up(void)
    {
            return system_state == SYSTEM_RUNNING;
    }
    EXPORT_SYMBOL(system_is_up);
在函数原型中,包含参数名称及其数据类型;

不要在函数原型中使用extern关键字,因为这会使行更长;

疑问:
"不要在函数原型中使用extern关键字"怎么理解?
那什么时候使用该关键字?
头文件中使用可以建议吗?
————————————————
版权声明:本文为CSDN博主「BurgessKzg」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/burgessKZG/article/details/106799667

标签
菜单分类