首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

与嵌入式新手分享Linux内核编码风格(2)

与嵌入式新手分享Linux内核编码风格(2)

第四章:命名

C 是一个简朴的语言,你的命名也应该这样。和Modula-2和Pascal程序员不同,C程序员不使用类似ThisVariableIsATemporaryCounter这样华丽的名字。C程序员会称那个变量为“tmp”,这样写起来会更容易,而且至少不会令其难于理解。

不过,虽然混用大小写的名字是不提倡使用的,但是全局变量还是需要一个具描述性的名字。称一个全局函数为“foo”是一个难以饶恕的错误。

全局变量(只有当你真正需要它们的时候再用它)需要有一个具描述性的名字,就像全局函数。如果你有一个可以计算活动用户数量的函数,你应该叫它“count_active_users()”或者类似的名字,你不应该叫它“cntuser()”。

在函数名中包含函数类型(所谓的匈牙利命名法)是脑子出了问题——编译器知道那些类型而且能够检查那些类型,这样做只能把程序员弄糊涂了。难怪微软总是制造出有问题的程序。

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

如果你怕混淆了你的本地变量名,你就遇到另一个问题了,叫做函数增长荷尔蒙失衡综合症。请看第六章(函数)。


                第五章:Typedef

不要使用类似“vps_t”之类的东西。
对结构体和指针使用typedef是一个错误。当你在代码里看到:

        vps_t a;

这代表什么意思呢?

相反,如果是这样

        struct virtual_container *a;

你就知道“a”是什么了。

很多人认为typedef“能提高可读性”。实际不是这样的。它们只在下列情况下有用:

(a) 完全不透明的对象(这种情况下要主动使用typedef来隐藏这个对象实际上是什么)。

    例如:“pte_t”等不透明对象,你只能用合适的访问函数来访问它们。

    注意!不透明性和“访问函数本身”是不好的。我们使用pte_t等类型的原因在于真的是完全没有任何共用的可访问信息。

(b) 清楚的整数类型,这样抽象层就可以帮助我们消除到底是"int"还是"long"的混淆。

    u8/u16/u32是完全没有问题的typedef,不过它们更符合(d)中所言,而不是这里。再次注意!要这样做,必须事出有因。如果某个变量是“unsigned long“,那么没有必要

        typedef unsigned long myflags_t;

    不过如果有一个明确的原因,比如它在某种情况下可能会是一个“unsigned int”而在其他情况下可能为“unsigned long”,那么就不要犹豫,请务必使用typedef。

(c) 当你使用sparse按字面的创建一个新类型来做类型检查的时候。

(d) 和标准C99类型相同的类型,在某些例外的情况下。

    虽然让眼睛和脑筋来适应新的标准类型比如“uint32_t”不需要花很多时间,可以有些人仍然拒绝使用它们。

    因此,Linux特有的等同于标准类型的“u8/u16/u32/u64”类型和它们的有符号类型是被允许的——尽管在你自己的新代码中,它们不是强制要求要使用的。

    当编辑已经使用了某个类型集的已有代码时,你应该遵循那些代码中已经做出的选择。

(e) 可以在用户空间安全使用的类型。

    在某些用户空间可见的结构体里,我们不能要求C99类型而且不能用上面提到的“u32”类型。因此,我们在与用户空间共享的所有结构体中使用__u32和类似的类型。
可能还有其他的情况,不过基本的规则是永远不要使用typedef,除非你可以明确的应用上述某个规则中的一个。

总的来说,如果一个指针或者一个结构体里的元素可以合理的被直接访问到,那么它们就不应该是一个typedef。


                第六章:函数

函数应该简短而漂亮,并且只完成一件事情。函数应该可以一屏或者两屏显示完(我们都知道ISO/ANSI屏幕大小是80x24),只做一件事情,而且把它做好。

一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理论上很简单的只有一个很长(但是简单)的case语句的函数,而且你需要在每个case里做很多很小的事情,这样的函数尽管很长,但也是可以的。

不过,如果你有一个复杂的函数,而且你怀疑一个天分不是很高的高中一年级学生可能甚至搞不清楚这个函数的目的,你应该更严格的遵守最大限制。使用辅助函数,并为之取个具描述性的名字(如果你觉得其对性能要求严格的话,你可以要求编译器将它们内联展开,它往往会比你更好的完成任务。)

函数的另外一个衡量标准是本地变量的数量。此数量不应超过5-10个,否则你的函数就有问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松的同时跟踪7个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你2个星期前做过的事情。

在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的EXPORT*宏应该紧贴在它的结束大括号之下。比如:

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

在函数原型中,包含函数名和它们的数据类型。虽然C语言里没有这样的要求,在Linux里这是提倡的做法,因为这样可以很简单的给读者提供更多的有价值的信息。


                第七章:集中的函数退出途径

虽然被某些人声称已经过时,但是goto语句的等价物还是经常被编译器所使用,具体形式是无条件跳转指令。当一个函数从多个位置退出并且需要做一些通用的清洁工作的时候,goto的好处就显现出来了。
理由是:
- 无条件语句容易理解和跟踪
- 嵌套程度减小
- 可以避免由于修改时忘记更新某个单独的退出点而导致的错误
- 减轻了编译器的工作,无需删除冗余代码;)

int fun(int a)
{
        int result = 0;
        char *buffer = kmalloc(SIZE);

        if (buffer == NULL)
                return -ENOMEM;

        if (condition1) {
                while (loop1) {
                        ...
                }
                result = 1;
                goto out;
        }
        ...
out:
        kfree(buffer);
        return result;
}


                第八章:注释

注释是好的,不过有过度注释的危险。永远不要在注释里解释你的代码是如何运作的:更好的做法是让别人一看你的代码就可以明白,解释写的很差的代码是浪费时间。
一般的,你想要你的注释告诉别人你的代码做了什么,而不是怎么做的。也请你不要把注释放在一个函数体内部:如果函数复杂到你需要独立的注释其中的一部分,你很可能需要回到第六章看一看。你可以做一些小注释来注明或警告某些很聪明(或者槽糕)的做法,但不要加太多。你应该做的,是把注释放在函数的头部,告诉人们它做了什么,也可以加上它做这些事情的原因。

当注释内核API函数时,请使用kernel-doc格式。请看
Documentation/kernel-doc-nano-HOWTO.txt和scripts/kernel-doc以获得详细信息。

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.
         */

注释数据也是很重要的,不管是基本类型还是衍生类型。为了方便实现这一点,每一行应只声明一个数据(不要使用逗号来一次声明多个数据)。这样你就有空间来为每个数据写一段小注释来解释它们的用途了。
继承事业,薪火相传
返回列表