当前位置: 华文问答 > 数码

Linux 内核的 C 代码风格是怎样的?

2015-04-17数码

关于Linux内核的代码风格,Linus Torvalds在很多年前就给出过「标准」,后来这个「标准」经过很多改进形成了现在的文档,保存在Linux内核源码的Documentation/process/coding- style.rst文件里:

注意我上面虽然用了「标准」两个字,但这并不是一个通用标准,这个标准或者说守则仅限于Linux内核这个项目。每个项目或者说每个大型项目都会有不止一个人在开发和维护,所以管理者为了维护代码的可读性,往往会定义一种项目范围内的成文或不成文的代码风格让提交代码的人遵守。

当然并不是所有的项目都这样严格,Linux算是对代码风格规定的比较成文的一个,以至于很多人在Linux内核项目外也习惯性沿用这种风格。当然如果你要给一个已有的项目贡献代码,最好尊重那个项目自己的编码习惯。如果项目里没有明确说明,可以通过观察相关代码的样子来判断。

任何的编程语言都有很多的编程风格,网上一直争吵不断的就有很多,比如:

  1. 用Tab缩进还是空格缩进好
  2. 缩进2个格还是4个格还是8个格的距离好
  3. 花括号是对齐还是不对齐
  4. 变量定义用大写还是下划线

……等等……

其实我想说这种萝卜白菜各有所爱的东西根本没有争论的必要,为这种事情争执只能说明闲得dan疼。但是对于一个具体项目而言,规定一个尽量统一的风格去遵守却是很有意义的。我就见过一些多人维护的项目里,一个程序文件由于不同人提交的不同patch被改的「面目全非」,虽然没有语法错误,但是让人看着就很烦躁。你再给它提patch的时候,只能尽量做到一个文件里前后是2格对齐的时候你也用2格,前后是8格对齐的时候你也用8格,上面是4格下面是8格对齐你要在中间加一行的时候就抛硬币吧。有人可能会说,你给它都改了统一一下格式不就得了?不,如果你一次性调整很多格式,那就是另一个patch了,大量的无关修改会严重影响你当前patch的意义,一个patch一般就做一件特定的事,如果你要大量调整格式最好用另外单独的patch,但是单纯调整格式的patch会让人觉得很……所以一般都是项目的维护者自己看不下去了统一调整一下格式,或维护者表示愿意让你帮忙调整一下格式(少见)。扯远了,言归正传,为了避免日后出现「面目全非」的代码格式,在统一要求和日常审核的时候最好有统一的「标准」,用什么作为标准并不十分重要(这一般由项目核心团队的审美观价值观来决定,其它参与者遵守就行),一个项目有一个统一标准比较重要。

而Linux内核的代码风格就有一个成文的规定,就是我一开始说的那个文档。而且看过我这篇回答的人:

应该也了解到Linux对于patch也是也一定的格式要求,而为了方便patch中代码格式的审核,还特意写了一个自动检查的程序在scripts目录下叫checkpatch.pl:

$ ./scripts/checkpatch.pl 0001-xfs-remove-the-XFS_IOC_FSSETDM-definitions.patch total: 0 errors, 0 warnings, 55 lines checked 0001-xfs-remove-the-XFS_IOC_FSSETDM-definitions.patch has no obvious style problems and is ready for submission.

它可以帮你先自己检查一下自己当前的patch格式是否符合Linux项目的统一要求。当然我们不得不说这个检查并不是那么绝对,即使有错误报出来也要人工看一下具体情况是否需要修改,这并不是一个严格的必须过审的步骤,目前更多的是一个辅助,特别是对新人来说,可以节省别人给你调整格式问题的麻烦。

关于Linux中C语言的格式的描述有很多,比如缩进格式,花括号格式,函数参数格式,变量和函数的定义要求等,举一些简单的例子,比如大家最关心的缩进,Linux内核要求统一使用Tab的8格宽度(当然显示出来是不是8格也要看你本地编辑器对Tab的定义),像这样(注意这里的点只是想表示一个格的宽度,并不是要在这点点):

int main() { ........if (a == 1) ................return 0; }

还有比如花括号的使用格式上,函数和非函数使用花括号不一样。函数的花括号独自占一行的行首对齐,像这样:

void func(int a) { .... } int main() { .... }

而非函数的花括号是不对齐的,像这样:

if (a == 1) { ... } else if (b == 0) { ... } else { ... } while (x == y) { ... } switch (action) { case KOBJ_ADD: return "add"; case KOBJ_REMOVE: return "remove"; default: return NULL; }

这些是比较显而易见的地方,当然除此之外还有很多要求,具体的我就不一一列举了,请自行查看上面的文档,以及参考已有的项目内的成熟代码。在那个文档里还「贴心」的给出了一个用于emacs的配置,可以帮助emacs用户配置好自己用于Linux内核代码风格的缩进等格式,我目前也是使用了这个。

最后还是特别声明,以上格式仅针对Linux内核项目,且并没有绝对的普适的优越性,所以没必要拿着到别处去要求别人都认同你的审美。当然如果你喜欢可以将其作为自己的默认风格。