Linux Kernel编码风格(中文版)

转自:http://learn.akae.cn/media/ch09.html

代码风格好不好就像字写得好不好看一样,如果一个公司招聘秘书,肯定不要字写得难看的,同理,代码风格糟糕的程序员肯定也是不称职的。虽然编译器不会挑剔难看的代码,照样能编译通过,但是和你一个team的其他程序员肯定受不了,你自己也受不了,写完代码几天之后再来看,自己都不知道自己写的是什么。Structureand Interpretation of Computer Programs里有句话说得好:“Thus,programs must bewritten for people to read, and only incidentally formachines toexecute.”代码主要为了是写给人看的,而不是写给机器看的,只是顺便也能用机器执行而已,如果是为了写给机器看那直接写机器码就好了,没必要用高级语言了。代码和语言文字一样是为了表达思想、记载信息,所以一定要写得清楚整洁才能有效地表达。正因为如此,在一个软件项目中,代码风格一般都用文档规定死了,所有参与项目的人不管他自己原来是什么风格,都要遵守统一的风格,例如Linux内核的Linux内核源代码的Documentation/CodingStyle就是这样一个文档。我们以内核为例来讲解编码风格的概念,并没有说内核风格就一定是最好的编码风格,但Linux内核项目如此成功,足以说明它的编码风格是最好的C语言编码风格之一了。

  1. 缩进和空白

    1. 关键字if,while, for与其后的控制表达式的(括号之间插入一个空格分隔,但括号内的表达式应紧贴括号。

    2. 双目运算符的两侧插入一个空格分隔,单目运算符和操作数之间不加空格。

    3. 后缀运算符和操作数之间也不加空格,例如取结构体成员s.a、函数调用foo(arg1)、取数组成员a[i]

    4. ,号和;号之后要加空格,这是英文的书写习惯,例如for(i=1; i<10; i++)foo(arg1,arg2)

    5. 以上关于双目运算符和后缀运算符的规则不是严格要求,有时候为了突出优先级也可以写得更紧凑一些,例如for(i=1; i<10; i++)distance= sqrt(x*x + y*y)等。但是省略的空格一定不要误导了读代码的人,例如a||b&& c很容易让人理解成错误的优先级。

    6. 由于标准的Linux终端是2480列的,接近或大于80个字符的较长语句要折行写,折行后用空格和上面的表达式或参数对齐。

    7. 较长的字符串可以断成多个字符串然后分行书写,例如:

printf("Thisis such a long sentence that "

"itcannot be held within a line\n");

C编译器会自动把相邻的多个字符串接在一起,以上两个字符串相当于一个字符串"Thisissuch a long sentence that it cannot be held within a line\n"

    1. 有的人喜欢在变量定义语句中用Tab字符,使变量名对齐,这样看起来也很好,但不是严格要求的。

内核关于缩进的规则有以下几条。

      1. 要用缩进体现出语句块的层次关系,使用Tab字符缩进,不能用空格代替Tab。在标准的Linux终端上,一个Tab看起来是8个空格的宽度,有些编辑器可以设置一个Tab看起来是几个空格的宽度,建议设成8,这样大的缩进使代码看起来非常清晰。规定不能用空格代替Tab主要是不希望空格和Tab混在一起做缩进,如果混在一起用了,在某些编辑器里把Tab的宽度改了就会看起来非常混乱。

      2. if/elsewhiledo/whileforswitch这些可以带语句块的语句,语句块的{}应该和关键字写在一起,用空格隔开,而不是单独占一行。内核的写法和TheC ProgrammingLanguage一致,好处是不必占用太多空行,使得一屏能显示更多代码。这两种写法用得都很广泛,只要在同一个项目中能保持统一就可以了。

      3. 函数定义的{}单独占一行,这一点和语句块的规定不同。

      4. switch和语句块里的casedefault对齐写,也就是说语句块里的casedefault相对于switch不往里缩进。

      5. 自己命名的标号(用于goto)必须顶头写不缩进,而不管标号下的语句缩进到第几层。

      6. 代码中每个逻辑段落之间应该用一个空行分隔开。例如每个函数定义之间应该插入一个空行,头文件、全局变量定义和函数定义之间也应该插入空行。

      7. 一个函数的语句列表如果很长,也可以根据相关性分成若干组,用空行分隔,这条规定不是严格要求,一般变量定义语句组成一组,后面要加空行,return之前要加空行


  1. 标识符命名

标识符命名应遵循以下原则:

    1. 标识符的命名要清晰明了,可以使用完整的单词和大家易于理解的缩写。短的单词可以通过去元音形成缩写,较长的单词可以取单词的头几个字母形成缩写,也可以采用大家基本认同的缩写。例如count写成cntblock写成blklength写成lenwindow写成winmessage写成msgtemporary可以写成temp,也可以进一步写成tmp

    2. 内核风格规定变量、函数和类型采用全小写加下划线的方式命名,常量(宏定义和枚举常量)采用全大写加下划线的方式命名。上面举例的函数名radix_tree_insert、类型名structradix_tree_root、常量名RADIX_TREE_MAP_SHIFT等。有一种变量命名风格叫匈牙利命名法(Hungariannotation),用变量名的前缀记录变量的类型,例如iCntpMsglpszBlk等,LinusLinux内核源代码的Documentation/CodingStyle中毫不客气地讽刺了微软发明的这一风格:“Encodingthe type of a function into the name (so-called Hungarian notation)isbrain damaged - the compiler knows the types anyway and can checkthose, and it onlyconfuses the programmer. No wonder MicroSoftmakes buggyprograms.”代码风格本来就是一个很有争议的问题,如果你接受本章介绍的内核风格,就不要使用大小写混合的变量命名方式[17],更不要使用匈牙利命名法。

    3. 全局变量和全局函数的命名一定要详细,不惜多用几个单词多写几个下划线,例如函数名radix_tree_insert,因为它们在整个项目的许多源文件中都会用到,必须让使用者明确这个变量或函数是干什么用的。局部变量和只在一个源文件中调用的内部函数的命名可以简略一些,但不能太短,不要使用单个字母做变量名,只有一个例外:用ijk做循环变量是可以的。

    4. 针对中国程序员的一条特别规定:禁止用汉语拼音作为标识符名称,可读性极差。


  1. 函数

每个函数都应该设计得尽可能简单,简单的函数才容易维护。应遵循以下原则:

    1. 实现一个函数只是为了做好一件事情,不要把函数设计成用途广泛、面面俱到的,这样的函数肯定会超长,而且往往不可重用,维护困难。

    2. 函数内部的缩进层次不宜过多,一般以少于4层为宜。如果缩进层次太多就说明设计得太复杂了,应该考虑分割成更小的函数来调用(这称为HelperFunction)。

    3. 函数不要写得太长,建议在24行的标准终端上不超过两屏,太长会造成阅读困难,如果一个函数超过两屏就应该考虑分割函数了。Linux内核源代码的Documentation/CodingStyle文件中特别说明,如果一个函数在概念上是简单的,只是长度很长,这倒没关系。例如函数由一个大的switch组成,其中有非常多的case,这是可以的,因为各个case之间互不影响,整个函数的复杂度只等于其中一个case的复杂度,这种情况很常见,例如TCP协议的状态机实现。

    4. 执行函数就是执行一个动作,函数名通常应包含动词,例如get_currentradix_tree_insert

    5. 比较重要的函数定义上面必须加注释,说此函数的功能、参数、返回值、错误码等。

    6. 另一种度量函数复杂度的办法是看有多少个局部变量,510个局部变量就已经很多了,局部变量再多就很难维护了,应该考虑分割函数。

你可能感兴趣的:(kernel)