linux内核的编码是一种极端情况
需要清晰明朗以供全世界的开发者学习修改对代码的质量要求较高
相信linus大神在长期接触各种各式代码后脾气会变得更暴躁就比如前段时间在某论坛痛斥C++
节选去除不适合PHP程序员阅读的部分
如果你开发PHP程序的核心代码比如框架尤其建议好好思考
虽然它和一些权威的代码规范比如discuz和zend的有所沖突但是依然能从中受益
linux kernel coding style (针对PHPer作了节选)
linux kernel coding style的中文译者:
中文版维护者 张乐 Zhang Le
中文版翻译者 张乐 Zhang Le
中文版校译者 王聪 Wang Cong
wheelz
管旭东 Xudong Guan
Li Zefan
Wang Chen
第一章缩进
制表符是个字符所以缩进也是个字符有些异端运动试图将缩进变为(乃至)个字符深这几乎相当于尝试将圆周率的值定义为
理由缩进的全部意义就在于清楚的定义一个控制块起止于何处尤其是当你盯着你的屏幕连续看了小时之后你将会发现大一点的缩进会使你更容易分辨缩进
现在有些人会抱怨个字符的缩进会使代码向右边移动的太远在个字符的终端屏幕上就很难读这样的代码这个问题的答案是如果你需要级以上的缩进不管用何种方式你的代码已经有问题了应该修正你的程序
在switch语句中消除多级缩进的首选的方式是让switch和从属于它的case标签对齐于同一列而不要两次缩进case标签比如
C++代码
switch (suffix) {
case G:
case g:
mem <<= ;
break;
case M:
case m:
mem <<= ;
break;
case K:
case k:
mem <<= ;
/* fall through */
default:
break;
}
不要把多个语句放在一行里除非你有什么东西要隐藏
C++代码
if (condition) do_this;
do_something_everytime;
也不要在一行里放多个赋值语句内核代码风格超级简单就是避免可能导致别人误读的表达式
除了注释文档和Kconfig之外不要使用空格来缩进前面的例子是例外是有意为之
选用一个好的编辑器不要在行尾留空格
第二章把长的行和字符串打散
代码风格的意义就在于使用平常使用的工具来维持代码的可读性和可维护性
每一行的长度的限制是列我们强烈建议您遵守这个惯例
长于列的语句要打散成有意义的片段每个片段要明显短于原来的语句而且放置的位置也明显的靠右同样的规则也适用于有很长参数列表的函数头长字符串也要打散成较短的字符串唯一的例外是超过列可以大幅度提高可读性并且不会隐藏信息的情况
C++代码
void fun(int a int b int c)
{
if (condition)
printk(KERN_WARNING Warning this is a long
parameters a: %u b: %u
c: %u \n a b c);
else
next_statement;
}
第三章大括号和空格的放置
C语言风格中另外一个常见问题是大括号的放置和缩进大小不同选择或弃用某种放置策略并没有多少技术上的原因不过首选的方式就像Kernighan和Ritchie展示给我们的是把起始大括号放在行尾而把结束大括号放在行首所以
C++代码
if (x is true) {
we do y
}
这适用于所有的非函数语句块(ifswitchforwhiledo)比如
C++代码
switch (action) {
case KOBJ_ADD:
return add;
case KOBJ_REMOVE:
return remove;
case KOBJ_CHANGE:
return change;
default:
return NULL;
}
不过有一个例外那就是函数函数的起始大括号放置于下一行的开头所以
C++代码
int function(int x)
{
body of function
}
全世界的异端可能会抱怨这个不一致性是……呃……不一致的不过所有思维健全的人都知道(a)K&R是_正确的_并且(b)K&R是正确的此外不管怎样函数都是特殊的(在C语言中函数是不能嵌套的)
注意结束大括号独自占据一行除非它后面跟着同一个语句的剩余部分也就是do语句中的while或者if语句中的else像这样
C++代码
do {
body of doloop
} while (condition);
和
C++代码
if (x == y) {
} else if (x > y) {
} else {
}
理由K&R
也请注意这种大括号的放置方式也能使空(或者差不多空的)行的数量最小化同时不失可读性因此由于你的屏幕上的新行是不可再生资源(想想行的终端屏幕)你将会有更多的空行来放置注释
当只有一个单独的语句的时候不用加不必要的大括号
C++代码
if (condition)
action();
这点不适用于本身为某个条件语句的一个分支的单独语句这时需要在两个分支里都使用大括号
C++代码
if (condition) {
do_this();
do_that();
} else {
otherwise();
}
空格
Linux内核的空格使用方式(主要)取决于它是用于函数还是关键字(大多数)关键字后要加一个空格值得注意的例外是sizeoftypeofalignof和__attribute__这些关键字某些程度上看起来更像函数(它们在Linux里也常常伴随小括号而使用尽管在C语言里这样的小括号不是必需的就像struct fileinfo info声明过后的sizeof info)
所以在这些关键字之后放一个空格
if switch case for do while
但是不要在sizeoftypeofalignof或者__attribute__这些关键字之后放空格例如
C++代码
s = sizeof(struct file);
不要在小括号里的表达式两侧加空格这是一个反例
C++代码
s = sizeof( struct file );
当声明指针类型或者返回指针类型的函数时*的首选使用方式是使之靠近变量名或者函
数名而不是靠近类型名例子
C++代码
char *linux_banner;
unsigned long long memparse(char *ptr char **retptr);
char *match_strdup(substring_t *s);
在大多数二元和三元操作符两侧使用一个空格例如下面所有这些操作符
C++代码
= + < > * / % | & ^ <= >= == != ? :
但是一元操作符后不要加空格
C++代码
& * + ~ ! sizeof typeof alignof __attribute__ defined
后缀自加和自减一元操作符前不加空格
++
前缀自加和自减一元操作符后不加空格
++
和>结构体成员操作符前后不加空格
不要在行尾留空白有些可以自动缩进的编辑器会在新行的行首加入适量的空白然后你就可以直接在那一行输入代码不过假如你最后没有在那一行输入代码有些编辑器就不会移除已经加入的空白就像你故意留下一个只有空白的行包含行尾空白的行就这样产生了
当git发现补丁包含了行尾空白的时候会警告你并且可以应你的要求去掉行尾空白不过如果你是正在打一系列补丁这样做会导致后面的补丁失败因为你改变了补丁的上下文
第四章命名
C是一个简朴的语言你的命名也应该这样和Modula和Pascal程序员不同C程序员不使用类似 ThisVariableIsATemporaryCounter 这样华丽的名字C程序员会称那个变量为tmp这样写起来会更容易而且至少不会令其难于理解
不过虽然混用大小写的名字是不提倡使用的但是全局变量还是需要一个具描述性的名字称一个全局函数为foo是一个难以饶恕的错误
全局变量(只有当你真正需要它们的时候再用它)需要有一个具描述性的名字就像全局函数如果你有一个可以计算活动用户数量的函数你应该叫它count_active_users()或者类似的名字你不应该叫它cntuser()
在函数名中包含函数类型(所谓的匈牙利命名法)是脑子出了问题——编译器知道那些类型而且能够检查那些类型这样做只能把程序员弄糊涂了难怪微软总是制造出有问题的程序
本地变量名应该简短而且能够表达相关的含义如果你有一些随机的整数型的循环计数器它应该被称为i叫它loop_counter并无益处如果它没有被误解的可能的话类似的tmp可以用来称呼任意类型的临时变量
如果你怕混淆了你的本地变量名你就遇到另一个问题了叫做函数增长荷尔蒙失衡综合症请看第六章(函数)
第六章函数
函数应该简短而漂亮并且只完成一件事情函数应该可以一屏或者两屏显示完(我们都知道ISO/ANSI屏幕大小是x)只做一件事情而且把它做好
一个函数的最大长度是和该函数的复杂度和缩进级数成反比的所以如果你有一个理论上很简单的只有一个很长(但是简单)的case语句的函数而且你需要在每个case里做很多很小的事情这样的函数尽管很长但也是可以的
不过如果你有一个复杂的函数而且你怀疑一个天分不是很高的高中一年级学生可能甚至搞不清楚这个函数的目的你应该严格的遵守前面提到的长度限制使用辅助函数并为之取个具描述性的名字
函数的另外一个衡量标准是本地变量的数量此数量不应超过-个否则你的函数就有问题了重新考虑一下你的函数把它分拆成更小的函数人的大脑一般可以轻松的同时跟蹤个不同的事物如果再增多的话就会糊涂了即便你聪颖过人你也可能会记不清你个星期前做过的事情
在源文件里使用空行隔开不同的函数
第八章注释
注释是好的不过有过度注释的危险永远不要在注释里解释你的代码是如何运作的更好
的做法是让别人一看你的代码就可以明白解释写的很差的代码是浪费时间
一般的你想要你的注释告诉别人你的代码做了什么而不是怎么做的也请你不要把注释
放在一个函数体内部如果函数复杂到你需要独立的注释其中的一部分你很可能需要回到
第六章看一看你可以做一些小注释来注明或警告某些很聪明(或者槽糕)的做法但不要
加太多你应该做的是把注释放在函数的头部告诉人们它做了什么也可以加上它做这
些事情的原因
当注释内核API函数时请使用kerneldoc格式请看
Documentation/kerneldocnanoHOWTOtxt 和 scripts/kerneldoc 以获得详细信息
Linux的注释风格是C/* */风格不要使用C风格// 注释
长(多行)的首选注释风格是
C++代码
/*
* This is the preferred style for multiline
* comments in the Linux kernel source code
* Please use it consistently
*
* Description: A column of asterisks on the left side
* with beginning and ending almostblank lines
*/
注释数据也是很重要的不管是基本类型还是衍生类型为了方便实现这一点每一行应只
声明一个数据(不要使用逗号来一次声明多个数据)这样你就有空间来为每个数据写一段
小注释来解释它们的用途了
对于PHPER来讲最好遵循PHPDOC风格的注释
第九章你已经把事情弄糟了
这没什么我们都是这样可能你的使用了很长时间Unix的朋友已经告诉你GNU emacs能
自动帮你格式化C源代码而且你也注意到了确实是这样不过它所使用的默认值和我们
想要的相去甚远(实际上甚至比随机打的还要差——无数个猴子在GNU emacs里打字永远不
会创造出一个好程序)(译注请参考Infinite Monkey Theorem)(对于PHPer可能就是zend studioeclipse之类的自动格式化工具)
所以你要么放弃GNU emacs要么改变它让它使用更合理的设定
第十二章宏枚举和RTL
用于定义常量的宏的名字及枚举里的标签需要大写
C++代码
#define CONSTANT x
宏的名字请用大写字母不过形如函数的宏的名字可以用小写字母
第十三章打印内核消息
内核开发者应该是受过良好教育的请一定注意内核信息的拼写以给人以好的印象不要
用不规范的单词比如dont而要用do not或者dont保证这些信息简单明了无
歧义
内核信息不必以句号(译注英文句号即点)结束
写出好的调试信息可以是一个很大的挑战当你写出来之后这些信息在远程除错的时候当DEBUG符号没有被定义的时候这些信息不应该被编译进内核里
就会成为极大的帮助
(也就是说默认地它们不应该被包含在内)(对于PHPer也是同样)
第十八章编辑器模式行和其他需要罗嗦的事情
有一些编辑器可以解释嵌入在源文件里的由一些特殊标记标明的配置信息比如emacs
能够解释被标记成这样的行
* mode: c *
或者这样的
/*
Local Variables:
compilecommand: gcc DMAGIC_DEBUG_FLAG fooc
End:
*/
Vim能够解释这样的标记
/* vim:set sw= noet */
这包括有关缩进和模式配置的标记人们可以使用他们自己定制的模
式或者使用其他可以产生正确的缩进的巧妙方法
不要在源代码中包含任何这样的内容每个人都有他自己的编辑器配置你的源文件不应
该覆盖别人的配置
简而言之个字符的缩进可以让代码更容易阅读还有一个好处是当你的函数嵌套太深的
时候可以给你警告留心这个警告