1.1.1 GNU编程风格
第一章 Linux 程序设计基础
1.1 编程风格
Linux作为GNU家族的一员,上面的源代码数以万计,而在阅读这些源代码时我们会发现,不同的源代码的美观程度和编程风格都不尽一样,有些代码,如glibc的代码:
static void
release_libc_mem (void)
{
/* Only call the free function if we still are running in mtrace mode. */
if (mallstream != NULL)
__libc_freeres ();
}
或者是Linux核心的代码:
static int do_linuxrc(void * shell)
{
static char *argv[] = { "linuxrc", NULL, };
close(0);close(1);close(2);
setsid();
(void) open("/dev/console",O_RDWR,0);
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp_init);
}
都令人看起来赏心悦目,而其它有些程序员写的程序则让人看起来直皱眉头,写作干净美观的代码,不仅仅使得代码更容易阅读,还使得代码能够成为一件艺术品。同微软的匈牙利命名法一样,在Linux上编程主要有两种编程风格: GNU风格和Linux核心风格,下面将分别介绍。
1.1.1 GNU编程风格
下面几条是基于GNU开放源代码方面的要求:
- 在任何情况下都不要引用有版权的源代码。
- 善意接受别人给您的程序添加的代码,但请记住检查其合法性,即是否也是 GNU 的。
- 编写日志文件(Changelog),这将使您的代码更容易维护。
下面是GNU对C程序的风格要求:
- 函数的开头的左花括号放到最左边,避免把任何其它的左花括号、左括号或者左方括号放到最左边。对于函数定义来说,把函数名的起始字符放到最左边也同样重要。这帮助任何寻找函数定义,并且可能有助于帮助某些工具识别它们。因此,正确的格式应该是:
static char *
concat (s1, s2) /* Name starts in column zero here */
char *s1, *s2;
{ /* Open brace in column zero here */
...
}
或者,如果希望使用标准C,定义的格式是:
static char *
concat (char *s1, char *s2)
{
...
}
如果参数不能够被美观地放在一行中,按照下面的方式把它们分开:
int
lots_of_args (int an_integer, long a_long, short a_short,
double a_double, float a_float)
...
对于函数体,我们希望它按照如下方式排版:
if (x < foo (y, z))
haha = bar[4] + 5;
else
{
while (z)
{
haha += foo (z, z);
z--;
}
return ++x + bar ();
}
在左括号之前以及逗号之后添加空格将使程序更加容易阅读。尤其是在逗号之后添加空格。当我们把一个表达式分成多行的时候,在操作符之前而不是之后分割。
if (foo_this_is_long && bar > win (x, y, z)
&& remaining_condition)
- 尽力避免让两个不同优先级的操作符出现在相同的对齐方式中。例如,不要象下面那样写:
mode = (inmode[j] == VOIDmode
|| GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])
? outmode[j] : inmode[j]);
应该附加额外的括号以使得文本缩进可以表示出这种嵌套:
mode = ((inmode[j] == VOIDmode
|| (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])))
? outmode[j] : inmode[j]);
- 按照如下方式排版do-while语句:
do
{
a = foo (a);
}
while (a > 0);
- 每个程序都应该以一段简短地、说明其功能的注释开头。 例如:`fmt - filter for simple filling of text'。
- 请为每个函数书写注释以说明函数做了些什么,需要哪些种类的参数,参数可能值的含义以及用途。如果按照常见的方式使用C语言类型,就没有必要逐字重写C参数声明的含义。如果它使用了任何非标准的东西,或者是可能导致函数不能工作的任何可能的值(例如,不能保证正确处理一个包含了新行的字符串),请确认对它们进行了说明。如果存在重要的返回值,也需要对其进行解释。
- 不要在跨越了行的声明中声明多个变量。在每一行中都以一个新的声明开头。例如,不应该:
int foo,
bar;
而应该:
int foo, bar;
或者:
int foo;
int bar;
如果它们是全局变量,在它们之中的每一个之前都应该添加一条注释。
- 当在一个if语句中嵌套了另一个if-else语句,总是用花括号把if-else括起来。因此,不要写:
if (foo)
if (bar)
win ();
else
lose ();
而总是要写:
if (foo)
{
if (bar)
win ();
else
lose ();
}
如果在else语句中嵌套了一个if语句,即可以像下面那样写else if:
if (foo)
...
else if (bar)
...
按照与then那部分代码相同的缩进方式缩进else if的then部分代码,也可以在花括号中像下面那样把if嵌套起来:
if (foo)
...
else
{
if (bar)
...
}
- 要在同一个声明中同时说明结构标识和变量或者结构标试和类型定义(typedef)。 单独地说明结构标试,而后用它定义变量或者定义类型。
- 尽力避免在if的条件中进行赋值。例如,不要写:
if ((foo = (char *) malloc (sizeof *foo)) == 0)
fatal ("virtual memory exhausted");
而要写:
foo = (char *) malloc (sizeof *foo);
if (foo == 0)
fatal ("virtual memory exhausted");
- 请在名字中使用下划线以分隔单词,坚持使用小写; 把大写字母留给宏和枚举常量,以及根据统一的惯例使用的前缀。
例如,应该使用类似ignore_space_change_flag的名字;不要使用类似iCantReadThis的名字。
- 用于标明一个命令行选项是否被给出的变量应该在选项含义的说明之后,而不是选项字符 之后,被命名。一条注释即应该说明选项的精确含义,还应该说明选项的字母。例如,
/* Ignore changes in horizontal whitespace (-b). */
int ignore_space_change_flag;