参考自李健的《编写高质量代码--改善C++程序的150个建议》
建议0 不要让main函数返回void
操作系统将main作为程序入口,main函数执行程序代码,最后返回程序的退出状态。但是C++中有一个好坏难定的规定:
在main函数中,return语句用于离开main函数(析构掉所有的具有动态生存时间的对象),并将其返回值作为参数来调用exit函数。如果函数执行到结尾而没有遇到return语
句,其效果等同于执行了return 0;。
也就是说编译器会协助完成返回值的问题。
然而,这样的坏处就是,当编译器不支持这个规定的时候,程序就会出错,比如
int main()
{
}
在VC下编译是成功的,在g++下编译,就会提示错误:‘main’必须返回’int‘
建议1 区分0的4个面孔
1 整型0
2 空指针NULL
3 字符串结束标记‘\0‘
‘ \0‘是一个字符,占8位,其二进制位表示是 00000000,在C/C++中,‘\0‘作为字符串的结束标记,是唯一的结束标记
4 逻辑FALSE/false
false/true是标准C++语言里新增的关键字,而FALSE/TRUE是通过#define定义的宏:
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#defineTRUE 1
#endif
也就是说FALSE/TRUE是int类型,而false/true是bool型,两者是不一样的,bool在C++里是占用1个字节
建议2 避免那些有运算符引起的混乱
=和== :在if语句中 比较两个表达式是否相等,
if( nValue == 0)
{ ... }
如果程序员出现疲劳或精神不集中,把’==‘写成’=‘,这样if(nValue = 0),那么这个if条件是恒成立,并且nValue被赋值为0,并且不会提示错误
这恐怕不是程序员想要的。。。
但如果把常量写在前面,如if(0 == nValue){ },这样如果程序员写成if(0 = nValue){ },编译器会直接的提示错误
但是对于&和&& 、|和|| 这类运算符,就需要平时养成谨慎的习惯了。
建议3 对表达式计算顺序不要向当然
让我们来看一个例子
if(nGrade & MASK == GRAND_ONE)
{...}
我们的本意是,通过nGrade和MASK取与,然后在比较是否等于GRAND_ONE,
可是,实际上上面的代码的真实效果是if( nGrade & (MASK == GRAND_ONE)){}
这个建议的核心是,不要吝啬括号,让语义表达的更准确,这样可以减少出错
如: a = p() + q() * r();
三个函数p() 、q() 、r()的执行顺序可能是6种组合中的一个,所以a的值是不确定的,在这种情况下,就需要明确一种执行顺序,
int x=p();
int y = q();
a = x+y*r();
类似的: expr1 ? expr2 : expr3
建议4 小心宏#define使用中的陷阱
定义宏时,要使用完备的括号:
比如定义两个参数相加, #define ADD(a,b) ((a)+(b))是一个安全的方式
使用宏时,不允许参数发生变化,
定义宏时,用大括号将宏所定义的多条表达式括起来
建议5 不要忘记指针变量的初始化
局部变量的指针未初始化,可能导致程序崩溃
全局变量的指针,编译器会悄悄完成变量的初始化(0)。
建议6 明晰逗号分隔表达式的奇怪之处
逗号表达式是从C继承来的,其中每个表达式都会被执行,不过,整个表达式的值仅是最右边表达式的结果
如:if(++x,--y,x<20 && y >0),该语句返回的是“ x<20 && y>0”与0比较的结果
另外,逗号表达式既可以用作左值,也可以用作右值。
建议7 时刻提防内存溢出
C语言中的字符串库没有响应的安全保护措施,strcpy、strcat等函数操作时没有检查缓冲区大小,容易引起安全问题
好的方法是,定义一个函数,并传递确定的长度,如:
const int DATA_LENGTH = 16;
void DataCopy(char* src ,int len)
{
char dst[DATA_LENGTH];
for(int i=0;src[i] != 0 && i<DATA_LENGTH; i++)
cout<<src[i]<<endl;
if(len < DATA_LENGTH)
strcpy(dst,src);
}
这里如果,src的长度是10,当i=10时,src[10]就已经越界了。。。
建议8 拒绝晦涩难懂的函数指针
比如 void (*p[10]) (void (*)() );
1 声明一个无参数、返回空的函数指针的typedef,如:typedef void (*pfv)();
2 声明一个指向参数为pfv且返回空的函数指针, 如 : typedef void (*pFun_taking_pfv) (pfv));
3 定义数组,pFun_taking_pfv p[10];
建议9 防止头文件重发包含
方式一:
#ifndef __TEST_H__
#define __TEST_H__
......
#endif
方式二:
#pragma once
优缺点:方式一的缺点是宏导致的编译时间长
方式二的缺点是只是针对物理路径相同的文件,而不是文件的内容