前几天有新成员问到我关于代码规范的问题。关于代码规范,google就能找到很多的文档,我这也有一些。其实不同的环境, 不同的公司有不同的代码规范,没有一个统一的规定。但为了促进团队间代码的交流,和方便以后比赛时队友之间的代码查错,我整理了一份规范,请大家尽量遵守。内容如下:
1. 空行(4条规则) :
空行起着分隔程序段落的作用,空行使得程序的布局更加清晰。
【1-1】在函数内部局部变量定义结束之后处理语句之前要加空行。
【1-2】在每个函数定义结束之后都要加空行。参见示例1-1(a)。
【1-3】函数返回语句和其他语句之间使用空行分开。
【1-4】在一个函数体内,逻辑上密切相关的语句之间不加空行,其它地方应加空行分隔。
// 空行 void function1( ) { … } // 空行 void function2( ) { … } // 空行 void function3( ) { … } // 空行 while (condition) { … // 空行 if (condition) { … } else { … } // 空行 … }
2.代码行(5条规则)
【2-1】一行代码只做一件事情,如只写一条语句。这样的代码容易阅读。
【2-2】if、for、while、do 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}表明是一个语句块。
【2-3】左花括号与语句同行(不要另起一行),右花括号要单独占一行。但是在do-while、struct和union及其后有‘;’的除外,要同在一行。
【2-4】switch语句中的每个case语句各占一行,当某个case语句不需要break语句最好加注释声明。
【2-5】并列的语句行应该按照字母顺序排序,如变量定义和switch中的case语句等。
if (condition) { //左括号不要另起一行 statement1; } else { statement2; } //右括号与相应语句对齐 do { statement3; } while();
3.代码行内的空格(7条规则)
【3-1】关键字之后要留空格。像int、const、case 等关键字之后至少要留一个空格,否则无法辨析关键字。
【3-2】函数名之后不要留空格,紧跟左括号“(”,以与关键字区别。例如:void calc(void);
【3-3】“,”之后要留空格,如function(x, y, z)。如果“;”不是一行的结束符号,其后要留空格。
如for( initialization; condition; update )。
【3-4】不要在单目运算符(如“!”、“~”、“++”、“--”、“&”)和其操作对象间加空格。
例如:!foo,++i,(long)getValue
【3-5】赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符。
如“=”、“+=”、“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。
【3-6】像“[]”、“.”、“->”这类操作符前后不加空格。
例如:big.bar,pFile->bar,big[bar]
【3-7】“(”的右边,“)”的左边不要加空格
//良好的代码 void func(int x, int y, int z); if(year >= 2000) if(a >= b && c <= d) for (i=0; i < 10; i++) x = a < b ? a : b; int *x = &y; array[5] = 0; a.function(); b->Function();
4.对齐(3条规则)
【4-1】程序的分界符“}”应独占一行并且位于同一列,同时与引用它的语句左对齐,分界符“{”不用独占一行紧跟在语句后。
【4-2】水平缩进每次使用四个空格即可(建议定义一个tab键为四个空格)。
【4-3】同属于一个语句块的代码对齐。
5.长行拆分(2条规则)
【5-1】代码行最大长度宜控制在70至80个字符以内。代码行不宜过长,否则不便于阅读,也不便于打印。
【5-2】长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
if ((very_longer_variable1 >= very_longer_variable2) && (very_longer_variable3 <= very_longer_variable4) && (very_longer_variable5 <= very_longer_variable6)) { dosomething; } virtual CMatrix CMultiplyMatrix (CMatrix leftMatrix, CMatrix rightMatrix); for (very_long_initialization; very_long_condition; very_long_update) { dosomething; } if ((very_longer_variable1 >= very_longer_variable2) && (very_longer_variable3 <= very_longer_variable4) && (very_longer_variable5 <= very_longer_variable6)) { dosomething; }
6.标识符命名(2条规则)
标识符的命名规范很复杂很繁琐,这里只针对ACM比赛列两条命名的基本原则,对该项感兴趣的同学请自行查阅相关资料:
【6-1】含义清晰,不易混淆。
例如动态规划时的状态数组可以命名为dp[][]或是f[][],深度优先搜索的函数名可以命名为DFS,线段树的结构体可以命名为segTree
【6-2】不与其它模块、函数的命名空间相冲突。 避免用map、max、abs、sort等标识符命名函数,避免和库函数冲突。
7.常量(3条规则)
C 语言可以用const 来定义常量,也可以用#define 来定义常量。但是前者比后者有更多的优点:
(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换过程中可能会产生意料不到的错误。
(2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
【7-1】尽量使用const定义常量替代宏定义常量。
【7-2】用常量定义数据范围,避免不小心把范围写错后修改一堆数组的大小,这里建议使用define。例如:
#define M 1000 int pre[M], next[M], dp[M][M], a[M][M]; vector <int> edge[M];
【7-3】函数宏的每个参数都要括起来。
例如:#define WEEKS_TO_DAYS(w) (w*7)
应该写成:#define WEEKS_TO_DAYS(w) ((w)*7)
这样在翻译totalDays=WEEKS_TO_DAYS(1+2)时,才能够正确地翻译成:(1+2)*7;否则将错误地翻译成1+2*7。
建议这里使用内联函数。如:
inline int lowbit(int x) { return x & (-x); }