最近在阅读别人给我的一个项目的代码,发现格式比较乱,导致阅读代码效率很低,特意学习了Linux内核代码规范Coding Style,将其整理在这里。

1.缩进

写代码这么多年,我的习惯一直是采用4字符缩进,为了防止在不同的编译器中对Tab键的解释不同,全部采用4个空格来缩进。而Linux内核中采用的是8的缩进,下面为其中摘取的一段描述:
  缩进的全部意义就在于清楚的定义一个控制块起止于何处。尤其是当你盯着你的屏幕连续看了 20 小时之后,你将会发现大一点的缩进会使你更容易分辨缩进。现在,有些人会抱怨 8 个字符的缩进会使代码向右边移动的太远,在 80 个字符的终端屏幕上就很难读这样的代码。这个问题的答案是,如果你需要 3 级以上的缩进,不管用何种方式你的代码已经有问题了,应该修正你的程序。

switch语句缩进
  对于switch语句的缩进我这么多年做法一直是错误的,在这里特意提出来:在 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;
}

2.把长的行和字符串打散

代码风格的意义就在于使用平常使用的工具来维持代码的可读性和可维护性。每一行的长度的限制是 80 列,长于 80 列的语句要打散成有意义的片段。子片段要明显短于母片段,并明显靠右。这同样适用于有着很长参数列表的函数头。

3.大括号的放置

C语言风格中另外一个常见问题是大括号的放置。本文采用的是把起始大括号放在行尾,而把结束大括号放在行首,如下所示:

if (x is true) {
	we do y
}

这适用于所有的非函数语句块 (if, switch, for, while, do)。比如:

int i = 0;
for (i=0; i<10; i++) {
	do some thing;
}

函数的起始大括号放置于下一行的开头,所以:

int function(int x)
{
	body of function
}

注意结束大括号独自占据一行,除非它后面跟着同一个语句的剩余部分,也就是 do 语句中的 “while” 或者 if 语句中的 “else”,像这样:

do {
	body of do-loop
} while (condition);

if (x == y) {
	..
} else if (x > y) {
	...
} else {
	....
}

4.空格

函数名后不要加空格,比如:

int function(int x)
{
	...
}

在下面关键字之后放一个空格:

if, switch, case, for, do, while

比如:

if (a) {
	...
}
switch (action) {
case KOBJ_ADD:
	return "add";
case KOBJ_REMOVE:
	return "remove";
case KOBJ_CHANGE:
	return "change";
default:
	return NULL;
}
for (condition) {
	...
}
do {
	body of do-loop
} while (condition);

细心的朋友应该注意到,上面的例子中小括号)和大括号{之间有一个空格。

不要在小括号里的表达式两侧加空格。这是一个 反例

s = sizeof( struct file );

在大多数二元三元操作符两侧使用一个空格,例如下面所有这些操作符::

=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :

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

&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined

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

i++  i--

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

++j  --j

.-> 结构体成员操作符前后不加空格。

p->member;

5.命名

在程序设计中命名一直提倡的是见名知意,也就是说看到一个函数名或者变量名时,能知道它所表达的含义。Linux内核采用_将多个单词或者缩写连接起来,不提倡采用大小写的方式,比如:

int count_active_users()
{
	...
}

从函数的字面意思就能知道此函数的作用是:计算活动用户数量。

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

6.函数

函数应该简短而漂亮,并且只完成一件事情。一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。应该将一个复杂的函数拆分成多个辅助函数,并为之取个具描述性的名字。
  函数的另外一个衡量标准是本地变量的数量。此数量不应超过 5-10 个,否则你的函数就有问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松的同时跟踪 7 个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你 2 个星期前做过的事情。

7.集中的函数退出途径

通常一个函数可能会存在多个退出点,这样就有两种情况:
  1)不需要做资源回收工作直接return;
  2)需要做资源回收工作,最好使用统一的退出途径,此时需要使用goto语句(只推荐在这种情况下使用goto语句)。使用 goto 的理由是:

  • 无条件语句容易理解和跟踪
  • 嵌套程度减小
  • 可以避免由于修改时忘记更新个别的退出点而导致错误
  • 让编译器省去删除冗余代码的工作
    一个例子:
int fun(void)
{
	char* foo = NULL;
	char* bar = NULL;

	foo = malloc(100);
	if (NULL == foo) {
		goto err_free_foo;
	}

	bar = malloc(100);
	if (NULL == bar) {
		goto err_free_bar;
	}

	...
	
 err_free_bar:
	free(bar);
 err_free_foo:
	free(foo);

	return 0;
}

未完待续…

Logo

更多推荐