Linux C语言的编程规范

嵌入式技术

1371人已加入

描述

Linux有独特的编程风格,在内核源代码目录Documentation/CodingStyle,详细描述代码风格。

建议大家可以去看一下,老外写技术文档还是很有意思的,上来就狂喷,“你不这样写就会完蛋,异教徒才不这样写……”,没有国内那么刻板,多阅读英语文档对技术增长很有帮助。

1. 命名规范

在一般编程中,习惯以如下方式命名宏、变量和函数:

#define PI 3.1415926 /*用大写字母代表宏 */
int minValue, maxValue; /*变量:第一个单词全小写,其后单词的第一个字母大写*/
void SendData (void); /* 函数:所有单词第一个字母都大写 */

这种通过单词之间通过首字母大写来区分的方式非常流行。通过第1个单词的首字母是否大写可以区分名称属于变量还是属于函数,而看到整串的大写字母可以断定为宏。

许多领域的程序开发都遵照此习惯。

但是Linux不以这种习惯命名,对于上面的一段程序,在Linux中它会被命名为:

#define PI 3.1415926
int min_value, max_value;
void send_data (void);

在上述命名方式中,宏还是一样用大写,但变量和函数名,不按照Windows所采用的用首字母大写来区分单词,而是采用下划线。而且Linux下命名,全局变量命名最好用长的准确的描述,局部变量最好简短,甚至直接用tmp,i之类的。

其实两种命名方式都行,写Liunx下的程序时,与Linux社区代码风格一致更好,但你用第一种我觉得也无伤大雅。

2.缩进

缩进统一使用"TAB",而不是空格括号。

另外提一句:在Linux下,"TAB"代表8个字符,而不是4个,Linux代码风格认为8个字符更能体现层次结构。文档里喷"TAB"为4字符的是异教徒,对于8字符在多层次时,代码太偏右的问题,文档又喷层次超过三层,你的代码就会完蛋,哈哈哈。

为了减少层次,在switch/case语句方面, Linux 建议switch和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;
}

3. Linux中代码括号“{”和“}”的使用原则

1)对于结构体、if/for/while/switch语句, “{”不另起一行,例如:

struct var_data {
    int len;
    char data[0];
};

if (a == b) {
    a = c;
    d = a;
}

for (i = 0; i < 10; i++) {
    a = c;
    d = a;
}

2)如果if、for循环后只有1行,不要加“{”和“}”,例如:

for (i = 0; i < 10; i++) {
     a = c;
}

应该改为:

for (i = 0; i < 10; i++) 
    a=c;
  1. if和else混用的情况下, else语句不另起一行,例如:
if (x == y){
    ...
} else if (x > y) {
    ...
} else {
    ...
}

4)对于函数, “{”另起一行,例如:

int add (int a, int b)
[
    return a + b;
}

4. 空格的使用

1)关键字后加空格

在这些关键字后面加空格:

if, switch, case, for, do, while

但是这些不加:

sizeof, typeof, alignof, attribute

例如:

s = sizeof(struct file);

2)括号内,紧挨着括号不加空格

错误示范:

s = sizeof( struct file );

3)对于指针,”*“号挨着名字而不是类型

例如:

char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);

4)操作符两侧加空格

以下二元或三元操作符两侧要加空格:

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

但是以下一元操作符,不加空格:

& * + - ~ ! sizeof typeof alignof attribute defined

自增自减符号。不加空格:

++ --

结构体成员操作符,不加空格:

'.' 和 "->"

不要在行尾加空格。

5. 函数

函数应该简洁明了,只做一件事。函数长度应该控制在一到两个屏幕显示范围内(ISO/ANSI屏幕大小是80x24)。

如果你有一个概念上简单的函数,那么使用一个更长的函数是可以的,比如函数里是case。

在源文件中,用空行分隔函数。如果函数被导出,它的EXPORT*宏应该紧跟在结束的函数大括号行之后。例如:

int system_is_up(void)
{
  return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);

6. goto语句的使用

用不用goto一直是一个著名的争议话题, Linux内核源代码中对goto的应用非常广泛,但是一般只限于错误处理中,其结构如:

if(register_a() != 0)
    goto err;
if (register_b() != 0)
    goto errl;
if(register_c() != 0)
    goto err2;
if (register_d () != 0)
    goto err3;
err3: 
    unregister_c();
err2: 
    unregister_b ();
errl: 
    unregister_a ();
err: 
    return ret;

这种将goto用于错误处理的用法实在是简单而高效,只需保证在错误处理时注销、资源释放等,与正常的注册、资源申请顺序相反。

7. 注释

Linux风格的注释是c89 "/ /”风格。不要使用c99风格的“//…”注释。

长(多行)注释的首选样式是:

/*
   * This is the preferred style for multi-line
   * comments in the Linux kernel source code.
   * Please use it consistently.
   *
   * Description:  A column of asterisks on the left side,
   * with beginning and ending almost-blank lines.
   */

8.do {} while(0) 语句

在Linux内核中,经常会看到do {} while(0)这样的语句。

许多人开始都会疑惑,认为do while(0)毫无意义,因为它只会执行一次,加不加do {} while(0)效果是完全一样的,其实do {} while(0)的用法主要用于宏定义中。

这里用一个简单的宏来演示:

#define SAFE_FREE (p) do{ free (p); p = NULL;}  while (0)

假设这里去掉do...while(0),即定义SAFE_DELETE为:

#define SAFE FREE (p) free (p); p = NULL;

那么以下代码:

if (NULL != p)
    SAFE_DELETE(p)
else
    .../* do something */

会被展开为:

if (NULL != P)
    free (p); p = NULL;
else
    .../* do something */

展开的代码中存在两个问题:

1)因为if分支后有两个语句,导致else分支没有对应的if,编译失败。

2)假设没有else分支,则SAFE_FREE中的第二个语句无论if测试是否通过,都会执行。

的确,将SAFE_FREE的定义加上{}就可以解决上述问题了,即:

#define SAFE_FREE (p) { free (p); p= NULL; }

这样,代码:

if (NULL != p)
    SAFE_DELETE(p)
else
    ... /* do something */

会被展开为:

if (NULL != p)
    { free (p); P = NULL; }
else
    .../* do something */

但是,在C程序中,在每个语句后面加分号是一种约定俗成的习惯,那么,如下代码:

if (NULL != p)
    SAFE_DELETE (p);
else
    .... /* do something */

将被扩展为:

if (NULL != p)
    { free (p); p = NULL; };
else
    .../* do something */

这样else分支就又没有对应的if了,编译将无法通过。

假设用了do {} while(0)语句,情况就不一样了,同样的代码会被展开为:

if (NULL != p)
    do{ free (p); p= NULL; } while (0);
else
    .../* do something */

而不会再出现编译问题。do{} while(0)的使用完全是为了保证宏定义的使用者能无编译错误地使用宏,它不对其使用者做任何假设。

9. 其他规定

1.原则上不使用typedef,Linux认为typedef会使可读写变差

2.内联函数通常不超过三行

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分