本文简短描述了 Linux 内核推荐的代码风格。代码风格是非常个性化的,但这是 Linux 内核必须维持的准则,对于很多其他领域的代码,该规范也具有参考意义。
首先,请打印出 GNU 代码规范,不要阅读,烧掉它们,这是一个很棒的象征性手势。
1 缩进
Tab 占 8 个字符,因此缩进也是 8 个字符。有一些异端运动试图减少缩进至 4 个字符(甚至 2 个字符),类似的尝试是将 PI 的值定义为 3。
原因:缩进的整体思想是明确定义控制块的开始和结束位置。特别是当你连续看屏幕 20 个小时时,你会发现当缩进值较大时,注意到缩进会更加轻松。
现在,有些人表示 8 个字符的缩进使代码向右移得太远,难以在宽度为 80 字符的终端屏幕上阅读。
这个问题的答案是:如果你需要三个以上的缩进级别,无论如何程序都会被搞砸,应当修改程序。
简而言之,8 字符缩进使代码更易于阅读,并且在函数嵌套过深时发出警告。注意这个警告。
减少 switch 语句中的多级缩进的首选方法是将 switch 与 case 标签在同一列对齐而不是缩进 case 标签:
switch (suffix) {case 'G':case 'g': mem <<= 30; break;case 'M':case 'm': mem <<= 20; break;case 'K':case 'k': mem <<= 10; /* fall through */default: break; }
除非要隐藏某些内容,否则不要将多个语句放在同一行上:
if (condition) do_this; do_something_everytime;
也不要将多个任务放在同一行上。内核代码风格非常简单。避免使用复杂的表达式。
除了在注释、文档和 Kconfig 之外,空格都不能用于缩进,上述示例被故意破坏了。
得到一个体面的编辑,不要在行尾留空白。
2 打破长行和长字符串
代码风格作用于常用工具,使工具增加可读性和可维护性。
行的长度限制为 80 列,这是一个强优先限值。
除非该行超过 80 列会显著提高可读性并且不会隐藏信息,长度超过 80 列的语句将被分成合理的块。后代始终比父代短很多,并且基本上位于右侧。具有长参数列表的函数头也是如此。但是切勿破坏诸如 printk 消息之类的用户可见的字符串,因为这会破坏为它们进行 grep 的能力。
3 放置大括号和空格
C 风格中经常出现的另一个问题是大括号的位置。
与缩进尺寸不同,没有什么技术上的原因让我们选择一种放置策略而不是另一种,但是正如 向我们展示的一样,首选方式是将开括号放在一行的最后,将闭括号放在新行:
和if (x is true) { we do y }
这实用与所有非函数语句块(if, switch, for, while, do
)。例如:
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 }
全世界的异端人士都声称这种矛盾是矛盾的,但是所有有正确思想的人们都知道 是正确的。
此外,函数还是很特殊的(你不能将它们嵌套在 C 代码里)。
请注意,闭括号所在的行是空的,除非在其后接同一语句的延续,即 while
在 do
语句后或 else
在 if
语句后,像这样:
do { body of do-loop } while (condition);
和这样:
if (x == y) { .. } else if (x > y) { ... } else { .... }
理由:。
另外请注意:这种括号放置策略还可以在不损害可读性的前提下,最大程度地减少空(或几乎空)行的数量。
因此,由于屏幕上的新行供应是不可再生资源(请在此处考虑 25 行高的终端屏幕),你讲有更多的空行可以用来编写注释。
不要在使用单个语句的地方不必要地使用大括号。
if (condition) action();
和:
if (condition) do_this();else do_that();
如果多个条件语句中只有一个分支是单个语句,则不适用该规则。在这种情况下,需要在所有的分支中都使用大括号:
if (condition) { do_this(); do_that(); } else { otherwise(); }
3.1 空格#
Linux 内核风格使用空格的样式(主要)取决于函数与关键字的用法。
在大多数关键字之后使用一个空格。值得注意的例外是 sizeof, typeof, alignof, __attribute__
,看起来有点像函数(通常在 Linux 中使用时需要加括号,虽然它们并没有要求,如在 struct fileinfo info;
声明之后使用 sizeof info
。
因此,在这些关键字之后使用一个空格:
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
后缀递增和递减一元运算符前没有空格:
++ --
前缀递增和递减一元运算符后没有空格:
++ --
使用 .
和 ->
运算符也不需要空格。
不要在行尾留下尾随空格。某些带有智能缩进的编辑器会在适当的时候在新行的开头插入空格,从而让你可以立即开始输入下一行代码。但是,如果最终没有在新行放置代码(留空行),某些编辑器不会删除空格,最终导致包含尾随空格的行。
Git 会警告你有关引入尾随空格的补丁,并且可以有选择地为你剥离尾随空格。但是如果应用一系列补丁,可能会由于上下文的更改导致该系列的后续补丁失败。
4 命名
C 是一门简洁的语言,所以命名也应该如此。与 Modula-2 和 Pascal 程序员不同,C 程序员不会使用诸如ThisVariableIsATemporaryCounter
之类的可爱命名。一个 C 程序员将命名该变量为 tmp
,这样更容易编写,但同时也更难理解。
虽然不赞成使用大小写混合的命名方式,但描述性名称命名全局函数是必须的。
全局变量(仅在确实需要它们时才使用)与全局函数一样,都需要使用具有描述性的名称命名。如果现在有一个函数用来统计活跃用户数,应该将它命名为 count_active_users()
或类似的,而不应该命名为 cntusr()
。
将函数的类型编码为名称(所谓的匈牙利命名法)是不好的——编译器无论如何都知道它们的类型并且可以检查它们,这样只会使程序员感到困惑。难怪 写出了很多古怪的程序。
局部变量名和应该简单明了,切中要害。如果你有一些随机的整数循环变量,可以将它命名为 i
。如果该程序没有可能被误解,命名为 loop_counter
是没有必要的。同样,tmp
几乎可以用于任何类型的保存临时值的变量。
如果你害怕混淆你的局部变量名,你会遇到另一个问题,它通常被称为“生长激素功能失调综合征”。参见第六章(函数)。
5 Typedefs
请不要使用诸如 vps_t
之类的类型别名。对结构体和指针使用此类别名是一种错误。当你在源码中看到
vps_t a;
这是什么意思?
相反,如果是这样
struct virtual_container *a;
你就能知道 a
实际上到底是什么。
许多人认为 typedefs
提高了可读性。但并不是这样,它们仅对以下内容有用:
完全不透明的对象(
typedef
用于隐藏对象的内容)。示例:
pte_t
等不透明的对象,你只能通过合适的访问函数进行访问。注意:不透明性和访问函数并不是一种好的方法。之所以将它们用于类似
pte_t
的对象,是因为它们确实存在不可访问的信息。明确整数类型,这种抽象有助于避免混淆
int
和long
。u8/u16/u32
是完美的typedefs
。注意:
如果如果有任何量是
unsigned long
,那就没有理由使用typedef unsigned long myflags_t
。但是如果有明确的理由说明为什么在某些情况下它可能是
unsigned long
,而在另一些配置下可能是unsigned long
,那就继续使用typedef
。当你使用
sparse
创建一个用于类型检查的新类型。在某些特殊情况下,与 C99 标准类型相同的新类型。
尽管只需要很短的时间就可以使眼睛和大脑适应像这样的标准类型
uint32_t
,但是无论如何,有些人还是反对使用它们。因此,Linux 特定类型
u8/u16/u32/u64
及其有符号类型被允许定义别名,尽管它们在新代码中不是必须的。当编辑已使用一种或另一组类型的现有代码时,应当遵循该代码中的现有选择。
在用户空间保持类型安全。
在用户空间可见的某些结构体中,我们不能使用 C99 标准类型,也不能使用类似
u32
的形式。因此,我们应当在所有与用户空间共享的结构的体中使用__u32
和类似类型。
也许还有其他情况,但是一个基本规则是:除非你可以清楚地匹配上述规则之一,否则不要使用 typedef
。
通常,指针或者具有可以合理地直接访问元素的结构体永远都不应该使用 typedef
。
6 函数
函数应该简洁而优美,每个函数只做一件事。它们应该适合一到两个文本(
相关推荐
0评论