引言
看武侠都知道,即便你的武艺再高,如果内力跟不上,那在对手面前也只是花拳绣腿。编程也一样,即便你写的逻辑再深、业务再复杂,如果你的代码没有人能看懂(过段时间自己也懵逼了),下次再解决类似问题又得花费很长时间来处理。由此可说明你没有一套好的内功心法,无法积攒自己的内功。
小测试
让我们先来做个测试。看你心中的高手(或者自己)符合以下几条。
- 真正的程序员没有进度表,只有讨好领导的马屁精才有进度表,真正的程序员会让领导提心吊胆。
- 真正的程序员不写使用说明书,用户应当自己去猜想程序的功能。
- 真正的程序员几乎不写代码的注释,如果注释很难写,它理所当然也很难读。
- 真正的程序员不画流程图,原始人和文盲才会干这事。
- 真正的程序员不看参考手册,新手和胆小鬼才会看。
- 真正的程序员不写文档也不需要文档,只有看不懂程序的笨蛋才用文档。
- 真正的程序员认为自己比用户更明白用户需要什么。
- 真正的程序员不接受团队开发的理念,除非他自己是头头。
- 真正的程序员的程序不会在第一次就正确运行,但是他们愿意守着机器进行若干个30小时的调试改错。
- 真正的程序员不会在上午 9:00 到下午 5:00 之间工作,如果你看到他在上午9:00 工作,这表明他从昨晚一直干到现在。
...
是否心中很信仰这样的程序员?的确,这样‘真正的’程序员无拘无束、行云流水,我一开始是很向往的。在拜读林锐的《高质量C++、C编程指南》之后,逐渐发现,若照此修炼,就只能花拳绣腿。因此练好内功,才能刚!
内功修炼法精简版(正文)
1.程序版式
【规则1】一行代码只做一件事,如只定义一个变量,或只写一条语句。便于阅读,便于注释。
int width; //宽度
int height; //高度
int depth; //深度
【建议】尽可能在定义变量的同时初始化该变量(就近原则)
int width = 10; //宽度
int height = 10; //高度
int depth = 10; //深度
【规则2】if、for、while、do 等语句自占一行,执行语句不得紧跟其后。不论 执行语句有多少都要加{}。
if (width < height)
{
dosomething();
}
【规则3】空格问题
1.关键字之后要留空格。象 const、virtual、inline、case 等关键字之后 至少要留一个空格。if、for、while 等关键字之后应留一个 空格再跟左括号‘(’,以突出关键字。
- ‘ ,’之后要留空格,如 Function(x, y, z)。如果‘;’不是一行的结束
符号,其后要留空格,如 for (initialization; condition; update)。 - 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。
...
详见如下代码
void Func1(int x, int y, int z); // 良好的风格
void Func1 (int x,int y,int z); // 不良的风格
if (year >= 2000) // 良好的风格
if(year>=2000) // 不良的风格
if ((a>=b) && (c<=d)) // 良好的风格
if(a>=b&&c<=d) // 不良的风格
for (i=0; i<10; i++) // 良好的风格
for(i=0;i<10;i++) // 不良的风格
for (i = 0; I < 10; i ++) // 过多的空格
x = a < b ? a : b; // 良好的风格
x=a<b?a:b; // 不好的风格
int *x = &y; // 良好的风格
int * x = & y; // 不良的风格
array[5] = 0; // 不要写成 array [ 5 ] = 0;
【规则4】内容对齐
void Function(int x)
{
... // program code
}
if (condition)
{
... // program code
}
else
{
... // program code
}
【规则5】长行拆分:长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以 便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
if ((very_longer_variable1 >= very_longer_variable12)
&& (very_longer_variable3 <= very_longer_variable14)
&& (very_longer_variable5 <= very_longer_variable16))
{
dosomething();
}
virtual CMatrix CMultiplyMatrix (CMatrix leftMatrix,
CMatrix rightMatrix);
for (very_longer_initialization;
very_longer_condition;
very_longer_update)
{
dosomething();
}
【规则6】注释
1、注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主, 注释太多了会让人眼花缭乱。注释的花样要少。
2、如果代码本来就是清楚的,则不必加注释。否则多此一举,令人厌烦。例如 i++; //i 加 1,多余的注释
3、当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。
/*
* 函数介绍:
* 输入参数:
* 输出参数:
* 返回值 :
*/
void Function(float x, float y, float z)
{
...
}
if (...)
{
...
while (...)
{
...
} // end of while
...
} // end of if
2.命名
【规则1】变量和参数用小写字母开头的单词组合而成。 例如:
BOOL flag;
int drawMode;
【规则2】常量全用大写的字母,用下划线分割单词。 例如:
const int MAX = 100;
const int MAX_LENGTH = 1000;
【规则3】静态变量加前缀 s_(表示 static)。 全局变量,则使全局变量加前缀 g_(表示 global)。
例如:
void Init(...)
{
static int s_initValue; // 静态变量
...
}
int g_howManyPeople; // 全局变量
int g_howMuchMoney; // 全局变量
3.表达式和基本语句
【规则1】不要有多用途的复合表达式。
例如:
d = (a = b + c) + r ;
该表达式既求 a 值又求 d 值。应该拆分为两个独立的语句:
a = b + c;
d = a + r;
【规则2】不可将布尔变量直接与 TRUE、FALSE 或者 1、0 进行比较。
假设布尔变量名字为 flag,它与零值比较的标准 if 语句如下:
if (flag) //表示flag为真
if (!flag) //表示 flag 为假
其它的用法都属于不良风格,例如:
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
【规则3】应当将整型变量用“==”或“!=”直接与0比较。
假设整型变量的名字为 value,它与零值比较的标准 if 语句如下:
if (value == 0)
if (value != 0)
不可模仿布尔变量的风格而写成
if (value) // 会让人误解 value 是布尔变量
if (!value)
【规则4】应当将指针变量用“==”或“!=”与NULL比较。
指针变量的零值是“空”(记为 NULL)。尽管 NULL 的值与 0 相同,但是两者意义不 同。假设指针变量的名字为 p,它与零值比较的标准 if 语句如下:
if (p == NULL) // p 与 NULL 显式比较,强调 p 是指针变量
if (p != NULL)
有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程 序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把 p 和 NULL 颠倒。编 译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为 NULL 不能被赋值。
比如OC的懒加载方法中:
if (nil == object)
{
object = [NSObject alloc] init];
...
}
【规则5】循环语句效率
1.在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的 循环放在最外层,以减少 CPU 跨切循环层的次数。
// 低效率
for (row=0; row<100; row++)
{
for ( col=0; col<5; col++ )
{
sum = sum + a[row][col];
}
}
// 高效率
for (col=0; col<5; col++ )
{
for (row=0; row<100; row++)
{
sum = sum + a[row][col];
}
}
2.如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。
// 效率低但代码简洁 若N较小适用,执行效率劣势不大
for (i=0; i<N; i++)
{
if (condition)
DoSomething();
else
DoOtherthing();
}
// 效率高但代码冗余 若N较大适用,执行效率较高
if (condition)
{
for (i=0; i<N; i++) DoSomething();
}
else
{
for (i=0; i<N; i++) DoOtherthing();
}
3.建议for语句的循环控制变量的取值采用“半开半闭区间”写法。
半开半闭区间写法:for (int x=0; x<N; x++)
闭区间写法: for (int x=0; x<=N-1; x++)
4.常量
const 与 #define 的比较
【规则1】能用const的地方尽量用const来的代替#define,由于宏常量没有数据类型,const定义的常量编译器可以进行数据类型的安全检查。
【规则2】把公开的常量放在声明文件中,不公开的常量放在实现文件中。
【规则3】如果某一常量与其它常量密切相关,应在定义中包含这种关系。例如:
const float RADIUS = 100;
const float DIAMETER = RADIUS * 2;
5.内存管理
【一】杜绝“野指针”
“野指针”不是 NULL 指针,是指向“垃圾”内存的指针。人们一般不会错用 NULL 指针,因为用 if 语句很容易判断。但是“野指针”是很危险的,if 语句对它不起作用。
成因:
1.指针未初始化。指针刚创建时不会自动置为NULL,需要置为NULL或指向合法内存。
char *p = NULL;
char *str = (char *) malloc(100);
2.指针p被free或者delete之后,没有置为NULL,会让人误以为p是个合法指针。错误案例如下:
char *p = (char *) malloc(100);
strcpy(p, “hello”);
free(p); // p 所指的内存被释放,但是 p 所指的地址仍然不变
...
if(p != NULL) // 没有起到防错作用
{
strcpy(p, “world”); // 出错
}
3.指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:
class A
{
public:
void Func(void){ cout << “Func of class A” << endl; }
};
void Test(void)
{
A *p;
{
A a;
p = &a; // 注意 a 的生命期
}
p->Func(); // p 是“野指针”
}
【二】malloc/free 的使用要点
函数 malloc 的原型如下:
void * malloc(size_t size);
用 malloc 申请一块长度为 length 的整数类型的内存,程序如下:
int *p = (int *) malloc(sizeof(int) * length);
【注1】malloc 返回值的类型是 void *,所以调用malloc要进行显示转换一下类型。
【注2】malloc 本身不识别申请的内存类型,只关心内存的总字节数。由于不同位数的操作系统数据的字节又不一样,所以需要使用sizeof来计算所占字节数。
函数 free 的原型如下:
void free( void * memblock );
为什么 free 函数不象 malloc 函数那样复杂呢?这是因为指针 p 的类型以及它所指 的内存的容量事先都是知道的,语句 free(p)能正确地释放内存。如果 p 是 NULL 指针, 那么 free 对 p 无论操作多少次都不会出问题。如果 p 不是 NULL 指针,那么 free 对 p 连续操作两次就会导致程序运行错误。
网友评论