Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49e48419 authored by Andy Deng's avatar Andy Deng Committed by Jonathan Corbet
Browse files

Documentation/zh_CN: update Chinese version CodingStyle



Chinese version CodingStyle is a little outdate, it should be updated.

This patch sync with the latest CodingStyle of all changes,
new chapters (chapter 19 and chapter 20) have been translated.

Signed-off-by: default avatarAndy Deng <theandy.deng@gmail.com>
Signed-off-by: default avatarJonathan Corbet <corbet@lwn.net>
parent d81749ea
Loading
Loading
Loading
Loading
+350 −231
Original line number Diff line number Diff line
@@ -25,20 +25,19 @@ Documentation/CodingStyle的中文翻译
		Linux内核代码风格

这是一个简短的文档,描述了 linux 内核的首选代码风格。代码风格是因人而异的,而且我
不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风格,
并且我也希望绝大多数其他代码也能遵守这个风格。请在写代码时至少考虑一下本文所述的
风格。
不愿意把自己的观点强加给任何人,但这就像我去做任何事情都必须遵循的原则那样,我也
希望在绝大多数事上保持这种的态度。请(在写代码时)至少考虑一下这里的代码风格。

首先,我建议你打印一份GNU代码规范,然后不要读。烧了它,这是一个具有重大象征性
意义的动作。
首先,我建议你打印一份 GNU 代码规范,然后不要读。烧了它,这是一个具有重大象征性意义
的动作。

不管怎样,现在我们开始:


		第一章:缩进

制表符是8个字符,所以缩进也是8个字符。有些异端运动试图将缩进变为4(乃至2)个字符
深,这几乎相当于尝试将圆周率的值定义为3。
制表符是 8 个字符,所以缩进也是 8 个字符。有些异端运动试图将缩进变为 4(甚至 2!)
个字符深,这几乎相当于尝试将圆周率的值定义为 3。

理由:缩进的全部意义就在于清楚的定义一个控制块起止于何处。尤其是当你盯着你的屏幕
连续看了 20 小时之后,你将会发现大一点的缩进会使你更容易分辨缩进。
@@ -50,8 +49,8 @@ Documentation/CodingStyle的中文翻译
简而言之,8 个字符的缩进可以让代码更容易阅读,还有一个好处是当你的函数嵌套太深的
时候可以给你警告。留心这个警告。

在switch语句中消除多级缩进的首选的方式是让“switch”和从属于它的“case”标签对齐于同
一列,而不要“两次缩进”“case”标签。比如:
 switch 语句中消除多级缩进的首选的方式是让 “switch” 和从属于它的 “case” 标签
对齐于同一列,而不要 “两次缩进” “case” 标签。比如:

	switch (suffix) {
	case 'G':
@@ -70,7 +69,6 @@ Documentation/CodingStyle的中文翻译
		break;
	}


不要把多个语句放在一行里,除非你有什么东西要隐藏:

	if (condition) do_this;
@@ -90,25 +88,16 @@ Documentation/CodingStyle的中文翻译

每一行的长度的限制是 80 列,我们强烈建议您遵守这个惯例。

长于80列的语句要打散成有意义的片段。每个片段要明显短于原来的语句,而且放置的位置
也明显的靠右。同样的规则也适用于有很长参数列表的函数头。长字符串也要打散成较短的
字符串。唯一的例外是超过80列可以大幅度提高可读性并且不会隐藏信息的情况。

void fun(int a, int b, int c)
{
	if (condition)
		printk(KERN_WARNING "Warning this is a long printk with "
						"3 parameters a: %u b: %u "
						"c: %u \n", a, b, c);
	else
		next_statement;
}
长于 80 列的语句要打散成有意义的片段。除非超过 80 列能显著增加可读性,并且不会隐藏
信息。子片段要明显短于母片段,并明显靠右。这同样适用于有着很长参数列表的函数头。
然而,绝对不要打散对用户可见的字符串,例如 printk 信息,因为这将导致无法 grep 这些
信息。

		第三章:大括号和空格的放置

C语言风格中另外一个常见问题是大括号的放置。和缩进大小不同,选择或弃用某种放置策
略并没有多少技术上的原因,不过首选的方式,就像KernighanRitchie展示给我们的,
把起始大括号放在行尾,而把结束大括号放在行首,所以:
略并没有多少技术上的原因,不过首选的方式,就像 KernighanRitchie 展示给我们的,
把起始大括号放在行尾,而把结束大括号放在行首,所以:

	if (x is true) {
		we do y
@@ -134,9 +123,9 @@ C语言风格中另外一个常见问题是大括号的放置。和缩进大小
		body of function
	}

全世界的异端可能会抱怨这个不一致性是……呃……不一致的,不过所有思维健全的人都知道
a)K&R是_正确的_,并且(b)K&R是正确的。此外,不管怎样函数都是特殊的(在C语言中
函数是不能嵌套的)。
全世界的异端可能会抱怨这个不一致性是……呃……不一致的,不过所有思维健全的人都知道
(a) K&R 是 _正确的_,并且 (b) K&R 是正确的。此外,不管怎样函数都是特殊的(C
函数是不能嵌套的)。

注意结束大括号独自占据一行,除非它后面跟着同一个语句的剩余部分,也就是 do 语句中的
“while” 或者 if 语句中的 “else”,像这样:
@@ -166,8 +155,14 @@ a)K&R是_正确的_,并且(b)K&R是正确的。此外,不管怎样函
	if (condition)
		action();

这点不适用于本身为某个条件语句的一个分支的单独语句。这时需要在两个分支里都使用大
括号。


	if (condition)
		do_this();
	else
		do_that();

这并不适用于只有一个条件分支是单语句的情况;这时所有分支都要使用大括号:

	if (condition) {
		do_this();
@@ -179,13 +174,16 @@ if (condition) {
		3.1:空格

Linux 内核的空格使用方式(主要)取决于它是用于函数还是关键字。(大多数)关键字后
要加一个空格。值得注意的例外是sizeof、typeof、alignof__attribute__,这些关键字
某些程度上看起来更像函数(它们在Linux里也常常伴随小括号而使用,尽管在C语言里这样
的小括号不是必需的,就像“struct fileinfo info”声明过后的“sizeof info”)。
要加一个空格。值得注意的例外是 sizeof、typeof、alignof__attribute__,这些
关键字某些程度上看起来更像函数(它们在 Linux 里也常常伴随小括号而使用,尽管在 C 里
这样的小括号不是必需的,就像 “struct fileinfo info” 声明过后的 “sizeof info”)。

所以在这些关键字之后放一个空格:

	if, switch, case, for, do, while

但是不要在 sizeof、typeof、alignof 或者 __attribute__ 这些关键字之后放空格。例如,

	s = sizeof(struct file);

不要在小括号里的表达式两侧加空格。这是一个反例:
@@ -204,15 +202,18 @@ Linux内核的空格使用方式(主要)取决于它是用于函数还是关
	=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :

但是一元操作符后不要加空格:

	&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined

后缀自加和自减一元操作符前不加空格:

	++  --

前缀自加和自减一元操作符后不加空格:

	++  --

“.”和“->”结构体成员操作符前后不加空格。
‘.’ 和 “->” 结构体成员操作符前后不加空格。

不要在行尾留空白。有些可以自动缩进的编辑器会在新行的行首加入适量的空白,然后你
就可以直接在那一行输入代码。不过假如你最后没有在那一行输入代码,有些编辑器就不
@@ -225,23 +226,23 @@ Linux内核的空格使用方式(主要)取决于它是用于函数还是关

		第四章:命名

C是一个简朴的语言,你的命名也应该这样。和Modula-2Pascal程序员不同,C程序员不使
用类似ThisVariableIsATemporaryCounter这样华丽的名字。C程序员会称那个变量为“tmp”
,这样写起来会更容易,而且至少不会令其难于理解。
C是一个简朴的语言,你的命名也应该这样。和 Modula-2Pascal 程序员不同,C 程序员
不使用类似 ThisVariableIsATemporaryCounter 这样华丽的名字。C 程序员会称那个变量
为 “tmp”,这样写起来会更容易,而且至少不会令其难于理解。

不过,虽然混用大小写的名字是不提倡使用的,但是全局变量还是需要一个具描述性的名字
。称一个全局函数为 “foo” 是一个难以饶恕的错误。

全局变量(只有当你真正需要它们的时候再用它)需要有一个具描述性的名字,就像全局函
数。如果你有一个可以计算活动用户数量的函数,你应该叫它“count_active_users()”或者
类似的名字,你不应该叫它“cntuser()”。
数。如果你有一个可以计算活动用户数量的函数,你应该叫它 “count_active_users()”
或者类似的名字,你不应该叫它 “cntuser()”。

在函数名中包含函数类型(所谓的匈牙利命名法)是脑子出了问题——编译器知道那些类型而
且能够检查那些类型,这样做只能把程序员弄糊涂了。难怪微软总是制造出有问题的程序。

本地变量名应该简短,而且能够表达相关的含义。如果你有一些随机的整数型的循环计数器
,它应该被称为“i”。叫它“loop_counter”并无益处,如果它没有被误解的可能的话。类似
的,“tmp”可以用来称呼任意类型的临时变量。
,它应该被称为 “i”。叫它 “loop_counter” 并无益处,如果它没有被误解的可能的话。
类似的,“tmp” 可以用来称呼任意类型的临时变量。

如果你怕混淆了你的本地变量名,你就遇到另一个问题了,叫做函数增长荷尔蒙失衡综合症
。请看第六章(函数)。
@@ -313,8 +314,8 @@ C是一个简朴的语言,你的命名也应该这样。和Modula-2和Pascal
道 ISO/ANSI 屏幕大小是 80x24),只做一件事情,而且把它做好。

一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理论上
很简单的只有一个很长(但是简单)的case语句的函数,而且你需要在每个case里做很多很
小的事情,这样的函数尽管很长,但也是可以的。
很简单的只有一个很长(但是简单)的 case 语句的函数,而且你需要在每个 case 里做
很多很小的事情,这样的函数尽管很长,但也是可以的。

不过,如果你有一个复杂的函数,而且你怀疑一个天分不是很高的高中一年级学生可能甚至
搞不清楚这个函数的目的,你应该严格的遵守前面提到的长度限制。使用辅助函数,并为之
@@ -323,8 +324,8 @@ C是一个简朴的语言,你的命名也应该这样。和Modula-2和Pascal

函数的另外一个衡量标准是本地变量的数量。此数量不应超过 5-10 个,否则你的函数就有
问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松的同时跟
7个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你2
个星期前做过的事情。
 7 个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你
2 个星期前做过的事情。

在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的 EXPORT* 宏应该紧贴
在它的结束大括号之下。比如:
@@ -344,8 +345,8 @@ EXPORT_SYMBOL(system_is_up);
虽然被某些人声称已经过时,但是 goto 语句的等价物还是经常被编译器所使用,具体形式是
无条件跳转指令。

当一个函数从多个位置退出并且需要做一些通用的清理工作的时候,goto的好处就显现出来

当一个函数从多个位置退出并且需要做一些类似清理的常见操作时,goto 语句就很方便了。
如果并不需要清理操作,那么直接 return 即可

理由是:

@@ -357,9 +358,10 @@ EXPORT_SYMBOL(system_is_up);
	int fun(int a)
	{
		int result = 0;
	char *buffer = kmalloc(SIZE);
		char *buffer;

	if (buffer == NULL)
		buffer = kmalloc(SIZE, GFP_KERNEL);
		if (!buffer)
			return -ENOMEM;

		if (condition1) {
@@ -367,14 +369,24 @@ int fun(int a)
				...
			}
			result = 1;
		goto out;
			goto out_buffer;
		}
		...
out:
	out_buffer:
		kfree(buffer);
		return result;
	}

一个需要注意的常见错误是“一个 err 错误”,就像这样:

	err:
		kfree(foo->bar);
		kfree(foo);
		return ret;

这段代码的错误是,在某些退出路径上 “foo” 是 NULL。通常情况下,通过把它分离成两个
错误标签 “err_bar:” 和 “err_foo:” 来修复这个错误。

		第八章:注释

注释是好的,不过有过度注释的危险。永远不要在注释里解释你的代码是如何运作的:更好
@@ -402,6 +414,15 @@ Linux的注释风格是C89“/* ... */”风格。不要使用C99风格“// ...
	 * with beginning and ending almost-blank lines.
	 */

对于在 net/ 和 drivers/net/ 的文件,首选的长(多行)注释风格有些不同。

	/* The preferred comment style for files in net/ and drivers/net
	 * looks like this.
	 *
	 * It is nearly the same as the generally preferred comment style,
	 * but there is no initial almost-blank line.
	 */

注释数据也是很重要的,不管是基本类型还是衍生类型。为了方便实现这一点,每一行应只
声明一个数据(不要使用逗号来一次声明多个数据)。这样你就有空间来为每个数据写一段
小注释来解释它们的用途了。
@@ -417,30 +438,44 @@ Linux的注释风格是C89“/* ... */”风格。不要使用C99风格“// ...
所以你要么放弃 GNU emacs,要么改变它让它使用更合理的设定。要采用后一个方案,你可
以把下面这段粘贴到你的 .emacs 文件里。

(defun linux-c-mode ()
  "C mode with adjusted defaults for use with the Linux kernel."
  (interactive)
  (c-mode)
  (c-set-style "K&R")
  (setq tab-width 8)
(defun c-lineup-arglist-tabs-only (ignored)
  "Line up argument lists by tabs, not spaces"
  (let* ((anchor (c-langelem-pos c-syntactic-element))
         (column (c-langelem-2nd-pos c-syntactic-element))
         (offset (- (1+ column) anchor))
         (steps (floor offset c-basic-offset)))
    (* (max steps 1)
       c-basic-offset)))

(add-hook 'c-mode-common-hook
          (lambda ()
            ;; Add kernel style
            (c-add-style
             "linux-tabs-only"
             '("linux" (c-offsets-alist
                        (arglist-cont-nonempty
                         c-lineup-gcc-asm-reg
                         c-lineup-arglist-tabs-only))))))

(add-hook 'c-mode-hook
          (lambda ()
            (let ((filename (buffer-file-name)))
              ;; Enable kernel mode for the appropriate files
              (when (and filename
                         (string-match (expand-file-name "~/src/linux-trees")
                                       filename))
                (setq indent-tabs-mode t)
  (setq c-basic-offset 8))

这样就定义了M-x linux-c-mode命令。当你hack一个模块的时候,如果你把字符串
-*- linux-c -*-放在头两行的某个位置,这个模式将会被自动调用。如果你希望在你修改
/usr/src/linux里的文件时魔术般自动打开linux-c-mode的话,你也可能需要添加
                (setq show-trailing-whitespace t)
                (c-set-style "linux-tabs-only")))))

(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)
			auto-mode-alist))
这会让 emacs 在 ~/src/linux-trees 目录下的 C 源文件获得更好的内核代码风格。

到你的.emacs文件里。

不过就算你尝试让emacs正确的格式化代码失败了,也并不意味着你失去了一切:还可以用“
indent”。
不过就算你尝试让 emacs 正确的格式化代码失败了,也并不意味着你失去了一切:还可以用
“indent”。

不过,GNU indent 也有和 GNU emacs 一样有问题的设定,所以你需要给它一些命令选项。不
过,这还不算太糟糕,因为就算是GNU indent的作者也认同K&R的权威性(GNU的人并不是
人,他们只是在这个问题上被严重的误导了),所以你只要给indent指定选项“-kr -i8”
过,这还不算太糟糕,因为就算是 GNU indent 的作者也认同 K&R 的权威性(GNU 的人并不是
人,他们只是在这个问题上被严重的误导了),所以你只要给 indent 指定选项 “-kr -i8”
(代表 “K&R,8 个字符缩进”),或者使用 “scripts/Lindent”,这样就可以以最时髦的方式
缩进源代码。

@@ -545,36 +580,52 @@ config ADFS_FS_RW
	#define CONSTANT 0x4000
	#define CONSTEXP (CONSTANT | 3)

cpp手册对宏的讲解很详细。Gcc internals手册也详细讲解了RTL(译注:register
5) 在宏里定义类似函数的本地变量时命名冲突:

	#define FOO(x)				\
	({					\
		typeof(x) ret;			\
		ret = calc_ret(x);		\
		(ret);				\
	})

ret 是本地变量的通用名字 - __foo_ret 更不容易与一个已存在的变量冲突。

cpp 手册对宏的讲解很详细。gcc internals 手册也详细讲解了 RTL(译注:register
transfer language),内核里的汇编语言经常用到它。


		第十三章:打印内核消息

内核开发者应该是受过良好教育的。请一定注意内核信息的拼写,以给人以好的印象。不要
用不规范的单词比如“dont”,而要用“do not”或者“don't”。保证这些信息简单、明了、
歧义。
用不规范的单词比如 “dont”,而要用 “do not”或者 “don't”。保证这些信息简单、明了、
歧义。

内核信息不必以句号(译注:英文句号,即点)结束。

在小括号里打印数字 (%d) 没有任何价值,应该避免这样做。

<linux/device.h> 里有一些驱动模型诊断宏,你应该使用它们,以确保信息对应于正确的
设备和驱动,并且被标记了正确的消息级别。这些宏有:dev_err(), dev_warn(),
dev_info()等等。对于那些不和某个特定设备相关连的信息,<linux/kernel.h>定义了
pr_debug()pr_info()。
设备和驱动,并且被标记了正确的消息级别。这些宏有:dev_err()dev_warn()
dev_info() 等等。对于那些不和某个特定设备相关连的信息,<linux/printk.h> 定义了
pr_notice()pr_info(),pr_warn(),pr_err() 和其他

写出好的调试信息可以是一个很大的挑战;当你写出来之后,这些信息在远程除错的时候
就会成为极大的帮助。当DEBUG符号没有被定义的时候,这些信息不应该被编译进内核里
(也就是说,默认地,它们不应该被包含在内)。如果你使用dev_dbg()或者pr_debug(),
就能自动达到这个效果。很多子系统拥有Kconfig选项来启用-DDEBUG。还有一个相关的惯例
是使用VERBOSE_DEBUG来添加dev_vdbg()消息到那些已经由DEBUG启用的消息之上。
写出好的调试信息可以是一个很大的挑战;一旦你写出后,这些信息在远程除错时能提供极大
的帮助。然而打印调试信息的处理方式同打印非调试信息不同。其他 pr_XXX() 函数能无条件地
打印,pr_debug() 却不;默认情况下它不会被编译,除非定义了 DEBUG 或设定了
CONFIG_DYNAMIC_DEBUG。实际这同样是为了 dev_dbg(),一个相关约定是在一个已经开启了
DEBUG 时,使用 VERBOSE_DEBUG 来添加 dev_vdbg()。

许多子系统拥有 Kconfig 调试选项来开启 -DDEBUG 在对应的 Makefile 里面;在其他
情况下,特殊文件使用 #define DEBUG。当一条调试信息需要被无条件打印时,例如,如果
已经包含一个调试相关的 #ifdef 条件,printk(KERN_DEBUG ...) 就可被使用。


		第十四章:分配内存

内核提供了下面的一般用途的内存分配函数:kmalloc(),kzalloc(),kcalloc()和
vmalloc()。请参考API文档以获取有关它们的详细信息。
内核提供了下面的一般用途的内存分配函数:
kmalloc(),kzalloc(),kmalloc_array(),kcalloc(),vmalloc() 和 vzalloc()。
请参考 API 文档以获取有关它们的详细信息。

传递结构体大小的首选形式是这样的:

@@ -586,6 +637,16 @@ vmalloc()。请参考API文档以获取有关它们的详细信息。
强制转换一个 void 指针返回值是多余的。C 语言本身保证了从 void 指针到其他任何指针类型
的转换是没有问题的。

分配一个数组的首选形式是这样的:

	p = kmalloc_array(n, sizeof(...), ...);

分配一个零长数组的首选形式是这样的:

	p = kcalloc(n, sizeof(...), ...);

两种形式检查分配大小 n * sizeof(...) 的溢出,如果溢出返回 NULL。


		第十五章:内联弊病

@@ -593,8 +654,8 @@ vmalloc()。请参考API文档以获取有关它们的详细信息。
函数有时候是恰当的(比如作为一种替代宏的方式,请看第十二章),不过很多情况下不是
这样。inline 关键字的过度使用会使内核变大,从而使整个系统运行速度变慢。因为大内核
会占用更多的指令高速缓存(译注:一级缓存通常是指令缓存和数据缓存分开的)而且会导
致pagecache的可用内存减少。想象一下,一次pagecache未命中就会导致一次磁盘寻址,
耗时5毫秒。5毫秒的时间内CPU能执行很多很多指令。
 pagecache 的可用内存减少。想象一下,一次pagecache未命中就会导致一次磁盘寻址,
耗时 5 毫秒。5 毫秒的时间内 CPU 能执行很多很多指令。

一个基本的原则是如果一个函数有 3 行以上,就不要把它变成内联函数。这个原则的一个例
外是,如果你知道某个参数是一个编译时常量,而且因为这个常量你确定编译器在编译时能
@@ -672,23 +733,81 @@ Vim能够解释这样的标记:
式,或者使用其他可以产生正确的缩进的巧妙方法。


		第十九章:内联汇编

在特定架构的代码中,你也许需要内联汇编来使用 CPU 接口和平台相关功能。在需要
这么做时,不要犹豫。然而,当 C 可以完成工作时,不要无端地使用内联汇编。如果
可能,你可以并且应该用 C 和硬件交互。

考虑去写通用一点的内联汇编作为简明的辅助函数,而不是重复写下它们的细节。记住
内联汇编可以使用 C 参数。

大而特殊的汇编函数应该放在 .S 文件中,对应 C 的原型定义在 C 头文件中。汇编
函数的 C 原型应该使用 “asmlinkage”。

你可能需要将你的汇编语句标记为 volatile,来阻止 GCC 在没发现任何副作用后就
移除了它。你不必总是这样做,虽然,这样可以限制不必要的优化。

在写一个包含多条指令的单个内联汇编语句时,把每条指令用引号字符串分离,并写在
单独一行,在每个字符串结尾,除了 \n\t 结尾之外,在汇编输出中适当地缩进下
一条指令:

	asm ("magic %reg1, #42\n\t"
	     "more_magic %reg2, %reg3"
	     : /* outputs */ : /* inputs */ : /* clobbers */);


		第二十章:条件编译

只要可能,就不要在 .c 文件里面使用预处理条件;这样做让代码更难阅读并且逻辑难以
跟踪。替代方案是,在头文件定义函数在这些 .c 文件中使用这类的条件表达式,提供空
操作的桩版本(译注:桩程序,是指用来替换一部分功能的程序段)在 #else 情况下,
再从 .c 文件中无条件地调用这些函数。编译器会避免生成任何桩调用的代码,产生一致
的结果,但逻辑将更加清晰。

宁可编译整个函数,而不是部分函数或部分表达式。而不是在一个表达式添加 ifdef,
解析部分或全部表达式到一个单独的辅助函数,并应用条件到该函数内。

如果你有一个在特定配置中可能是未使用的函数或变量,编译器将警告它定义了但未使用,
标记这个定义为 __maybe_unused 而不是将它包含在一个预处理条件中。(然而,如果
一个函数或变量总是未使用的,就直接删除它。)

在代码中,可能的情况下,使用 IS_ENABLED 宏来转化某个 Kconfig 标记为 C 的布尔
表达式,并在正常的 C 条件中使用它:

	if (IS_ENABLED(CONFIG_SOMETHING)) {
		...
	}

编译器会无条件地做常数合并,就像使用 #ifdef 那样,包含或排除代码块,所以这不会
带来任何运行时开销。然而,这种方法依旧允许 C 编译器查看块内的代码,并检查它的正确
性(语法,类型,符号引用,等等)。因此,如果条件不满足,代码块内的引用符号将不存在,
你必须继续使用 #ifdef。

在任何有意义的 #if 或 #ifdef 块的末尾(超过几行),在 #endif 同一行的后面写下
注释,指出该条件表达式被使用。例如:

	#ifdef CONFIG_SOMETHING
	...
	#endif /* CONFIG_SOMETHING */


		附录 I:参考

The C Programming Language, 第二版, 作者Brian W. Kernighan和Denni
M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (软皮),
0-13-110370-9 (硬皮). URL: http://cm.bell-labs.com/cm/cs/cbook/
The C Programming Language, 第二版
作者:Brian W. Kernighan 和 Denni M. Ritchie.
Prentice Hall, Inc., 1988.
ISBN 0-13-110362-8 (软皮), 0-13-110370-9 (硬皮).

The Practice of Programming 作者Brian W. Kernighan和Rob Pike.  Addison-Wesley,
Inc., 1999.  ISBN 0-201-61586-X.  URL: http://cm.bell-labs.com/cm/cs/tpop/
The Practice of Programming
作者:Brian W. Kernighan 和 Rob Pike.
Addison-Wesley, Inc., 1999.
ISBN 0-201-61586-X.

cppgccgcc internalsindent的GNU手册——和K&R及本文相符合的部分,全部可以在
http://www.gnu.org/manual/找到
GNU 手册 - 遵循 K&R 标准和此文本 - cpp, gcc, gcc internals and indent,
都可以从 http://www.gnu.org/manual/ 找到

WG14是C语言的国际标准化工作组,URL: http://www.open-std.org/JTC1/SC22/WG14/

Kernel CodingStyle,作者 greg@kroah.com 发表于OLS 2002:
http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/

--
最后更新于2007年7月13日。