北京治白癜风去哪家医院最好 https://jbk.39.net/yiyuanzaixian/bjzkbdfyy/一、缩进
c语言图标
缩进是4个字符,在switch语句中消除多级缩进的首选的方式是让“switch”和从属于它的“case”标签对齐于同一列,而不要“两次缩进”“case”标签。比如:
switch(suffix){
caseG
/p>
caseg
/p>
mem=30;
break;
caseM
/p>
casem
/p>
mem=20;
break;
caseK
/p>
casek:
mem=10;
/*fallthrough*/
default:
break;
}
不要把多个语句放在一行里,除非你有什么东西要隐藏:
if(condition)do_this;
do_something_everytime;
也不要在一行里放多个赋值语句。
除了注释、文档和Kconfig之外,不要使用空格来缩进,前面的例子是例外,是有意为之,选用一个好的编辑器,不要在行尾留空格。
二、把长的行和字符串打散
代码风格的意义就在于使用平常使用的工具来维持代码的可读性和可维护性。每一行的长度的限制是80列,我们强烈建议您遵守这个惯例。长于80列的语句要打散成有意义的片段。每个片段要明显短于原来的语句,而且放置的位置也明显的靠右。同样的规则也适用于有很长参数列表的函数头。长字符串也要打散成较短的字符串。唯一的例外是超过80列可以大幅度提高可读性并且不会隐藏信息的情况。
voidfun(inta,intb,intc)
{
if(condition)
printk(KERN_WARNING"Warningthisisalongprintkwith"
"3parametersa:%ub:%u"
"c:%u\n",a,b,c);
else
next_statement;
}
三、大括号和空格的放置
C语言风格中另外一个常见问题是大括号的放置。把起始大括号放在行尾,而把结束大括号放在行首,所以:
if(xistrue){
wedoy
}
这适用于所有的非函数语句块(if、switch、for、while、do)。比如:
switch(action){
caseKOBJ_ADD:
return"add";
caseKOBJ_REMOVE:
return"remove";
caseKOBJ_CHANGE:
return"change";
default:
returnNULL;
}
不过,有一个例外,那就是函数:函数的起始大括号放置于下一行的开头,所以:
intfunction(intx)
{
bodyoffunction
}
注意结束大括号独自占据一行,除非它后面跟着同一个语句的剩余部分,也就是do语句中的“while”或者if语句中的“else”,像这样:
do{
bodyofdo-loop
}while(condition);
和
if(x==y){
..
}elseif(xy){
...
}else{
....
}
理由:
也请注意这种大括号的放置方式也能使空(或者差不多空的)行的数量最小化,同时不失可读性。因此,由于你的屏幕上的新行是不可再生资源(想想25行的终端屏幕),你将会有更多的空行来放置注释。
当只有一个单独的语句的时候,不用加不必要的大括号。
if(condition)
action();
这点不适用于本身为某个条件语句的一个分支的单独语句。这时需要在两个分支里都使用大括号。
if(condition){
do_this();
do_that();
}else{
otherwise();
}
四、命名
C是一个简朴的语言,你的命名也应该这样。C程序员不使用类似ThisVariableIsATemporaryCounter这样华丽的名字。C程序员会称那个变量为“tmp”
,这样写起来会更容易,而且至少不会令其难于理解。
不过,虽然混用大小写的名字是不提倡使用的,但是全局变量还是需要一个具描述性的名字。称一个全局函数为“foo”是一个难以饶恕的错误。
全局变量(只有当你真正需要它们的时候再用它)需要有一个具描述性的名字,就像全局函数。如果你有一个可以计算活动用户数量的函数,你应该叫它“count_active_users()”或者类似的名字,你不应该叫它“cntuser()”。
本地变量名应该简短,而且能够表达相关的含义。如果你有一些随机的整数型的循环计数器,它应该被称为“i”。叫它“loop_counter”并无益处,如果它没有被误解的可能的话。类似
的,“tmp”可以用来称呼任意类型的临时变量。
五、函数
函数应该简短而漂亮,并且只完成一件事情。函数应该可以一屏或者两屏显示完(我们都知道ISO/ANSI屏幕大小是80x24),只做一件事情,而且把它做好。
一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理论上很简单的只有一个很长(但是简单)的case语句的函数,而且你需要在每个case里做很多很小的事情,这样的函数尽管很长,但也是可以的。不过,如果你有一个复杂的函数,而且你怀疑一个天分不是很高的高中一年级学生可能甚至搞不清楚这个函数的目的,你应该严格的遵守前面提到的长度限制。使用辅助函数,并为之取个具描述性的名字(如果你觉得它们的性能很重要的话,可以让编译器内联它们,这样的效果往往会比你写一个复杂函数的效果要好。)
函数的另外一个衡量标准是本地变量的数量。此数量不应超过5-10个,否则你的函数就有问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松的同时跟踪7个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你2个星期前做过的事情。
在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的EXPORT*宏应该紧贴在它的结束大括号之下。比如:
intsystem_is_up(void)
{
returnsystem_state==SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);
在函数原型中,包含函数名和它们的数据类型。虽然C语言里没有这样的要求,在Linux里这是提倡的做法,因为这样可以很简单的给读者提供更多的有价值的信息。
六、宏和枚举
用于定义常量的宏的名字及枚举里的标签需要大写。
#defineCONSTANT0x
在定义几个相关的常量时,最好用枚举。宏的名字请用大写字母,不过形如函数的宏的名字可以用小写字母。
一般的,如果能写成内联函数就不要写成像函数的宏。
含有多个语句的宏应该被包含在一个do-while代码块里:
#definemacrofun(a,b,c)\
do{\
if(a==5)\
do_this(b,c);\
}while(0)
使用宏的时候应避免的事情:
1)影响控制流程的宏:
#defineFOO(x)\
do{\
if(blah(x)0)\
return-EBUGGERED;\
}while(0)
非常不好。它看起来像一个函数,不过却能导致“调用”它的函数退出;不要打乱读者大脑里的语法分析器。
2)依赖于一个固定名字的本地变量的宏:
#defineFOO(val)bar(index,val)
可能看起来像是个不错的东西,不过它非常容易把读代码的人搞糊涂,而且容易导致看起来不相关的改动带来错误。
3)作为左值的带参数的宏:FOO(x)=y;如果有人把FOO变成一个内联函数的话,这种用法就会出错了。
4)忘记了优先级:使用表达式定义常量的宏必须将表达式置于一对小括号之内。带参数的宏也要注意此类问题。
#defineCONSTANT0x
#defineCONSTEXP(CONSTANT
3)
七、函数返回值及命名
函数可以返回很多种不同类型的值,最常见的一种是表明函数执行成功或者失败的值。这样的一个值可以表示为一个错误代码整数(-Exxx=失败,0=成功)或者一个“成功”布尔值(0=失败,非0=成功)。
混合使用这两种表达方式是难于发现的bug的来源。如果C语言本身严格区分整形和布尔型变量,那么编译器就能够帮我们发现这些错误……不过C语言不区分。为了避免产生这种bug,请遵循下面的惯例:
如果函数的名字是一个动作或者强制性的命令,那么这个函数应该返回错误代码整数。如果是一个判断,那么函数应该返回一个“成功”布尔值。比如,“addwork”是一个命令,所以add_work()函数在成功时返回0,在失败时返回-EBUSY。类似的,因为“PCIdevicepresent”是一个判断,所以pci_dev_present()函数在成功找到一个匹配的设备时应该返回1,如果找不到时应该返回0。
八、内存分配
内核提供了下面的一般用途的内存分配函数:kmalloc(),kzalloc(),kcalloc()和
vmalloc()。请参考API文档以获取有关它们的详细信息。
传递结构体大小的首选形式是这样的:
p=kmalloc(sizeof(*p),...);
另外一种传递方式中,sizeof的操作数是结构体的名字,这样会降低可读性,并且可能会引入bug。有可能指针变量类型被改变时,而对应的传递给内存分配函数的sizeof的结果不变。
强制转换一个void指针返回值是多余的。C语言本身保证了从void指针到其他任何指针类型的转换是没有问题的。
九、内联弊端
有一个常见的误解是内联函数是gcc提供的可以让代码运行更快的一个选项。虽然使用内联函数有时候是恰当的(比如作为一种替代宏的方式,请看第十三章),不过很多情况下不是这样。inline关键字的过度使用会使内核变大,从而使整个系统运行速度变慢。因为大内核会占用更多的指令高速缓存(译注:一级缓存通常是指令缓存和数据缓存分开的)而且会导致pagecache的可用内存减少。想象一下,一次pagecache未命中就会导致一次磁盘寻址,将耗时5毫秒。5毫秒的时间内CPU能执行很多很多指令。
一个基本的原则是如果一个函数有3行以上,就不要把它变成内联函数。这个原则的一个例外是,如果你知道某个参数是一个编译时常量,而且因为这个常量你确定编译器在编译时能优化掉你的函数的大部分代码,那仍然可以给它加上inline关键字。kmalloc()内联函数就是一个很好的例子。
人们经常主张给static的而且只用了一次的函数加上inline,如此不会有任何损失,因为没有什么好权衡的。虽然从技术上说这是正确的,但是实际上这种情况下即使不加inlinegcc也可以自动使其内联。而且其他用户可能会要求移除inline,由此而来的争论会抵消inline自身的潜在价值,得不偿失。