由美国人Andrew Koenig编著的C Traps and Pitfalls是一本C语言学习者非常值得一看的书,它上面总结了C程序员在写C程序时经常犯的一些错误。笔者读了一遍还觉得不过瘾,于是又读了一遍,并且把看到的关键地方和笔者的一些心得记录下来,供同行参考。
1.1
赋值=和比较== 对于许多的C程序员,特别是新手,经常犯的一个错误就是把比较==写成了赋值=,而这种逻辑上的错误是不容易被编译器识别的。比如下面的语句
while ( c = ' ' || c == '\t' || c == '\n' )
c = getc(f);
该语句本意是要跳过空格,制表符,以及换行符,可是第一个c = ' '把比较写成了赋值,于是c的值为32(空格的ASCII码),最终得出的结果永远为真,于是循环一直进行下去,成为一个死循环。为了避免这样的问题,我们在写判断语句的时候,如果有常量或者表达式,通常都会写成这样:
while ( ' ' == c || '\t == c || '\n' == c )
c = getc(f);
有的人初看这样的写法感到很怪,其实在笔者看来这样的写法是良好的写法,也是正确的写法,把常量或者表达式放在比较符号==的前面可以有效的防止把比较语句写成赋值语句,因为常量或者表达式是不能作为左值的,如果上面的语句写成了
while ( ' ' = c || '\t == c || '\n' == c )
c = getc(f);
由于常量不能作为左值,所以上面的语句是通过不了编译器的检查的,这样就不容易犯错。
有的人为了程序的简洁,根据c语言中非0即为真的特点,在写判断语句的时候,没有显式的和0进行比较,比如下面的例子:
if ( x = y )
foo();
上面的意思是把y赋值给x然后再和0进行比较,有的编译器会对这样的写法提出警告,然而这样的写法不仅会让有的编译器误解,也很容易让读代码的人误解,对于这样的形式,我们最好写成下面的样子:
if ( (x = y ) != 0 )
foo();
这样的写法使代码的意图一目了然,是值得提倡的写法。
1.2
按位或 | 和逻辑 ||, 按位与 & 和逻辑&& 这两对也是程序员容易混淆的地方,将它们混淆,很容易产生逻辑上的错误。
1.3
词法分析中的"贪心法"("大嘴法") Kernighan & Ritchie 对这个方法的描述:如果编译器输入流截止至某个字符之前都已经被分解为一个个符号,那么下一个符号将包括从该字符之后可能组成一个符号的最长字符串。通俗一点来说,就是编译器从左到右一个一个的读入字符,如果已经读入的字符可能构成一个有意义的符号,那么继续读入,直到已经读入的字符不可能再组成一个有意义的符号为止,就像一张大嘴一样,能吃多少吃多少。比如下面的语句:
a+++b;
按照大嘴法,上面的a++构成一个有意义的符号,如果再读入一个,就不能构成有意义的符号了,所以上面的语句应该理解为(a++) + b;
1.4
整形常量 整形常量由于进制的不同,也容易造成混淆,比如101,分不清楚是10进制的一百零一,还是二进制的101,有的人在写程序的时候,为了对齐好看,容易将10进制误写成8进制,比如
struct {
int num;
char *description;
}parttab[] = {
041, "okokok",
033, "abcdef",
255, "xxxyisdf",
582, "sfsdfsdf"
};
本来这里要写成十进制的41和33,结果因为美观对齐,误写成了8进制的041和033。
1.5
字符和字符串
在c语言中,字符代表一个整数,在计算机内存中存储的是这个字符的ASCII值,而字符串是一个指针。
‘c'和"c"是截然不同的,'c'代表着整数99,而"c"代表一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制为0的字符'\0'初始化。
"c"在内存中表示如下
它占用了2个字节,一个是99,一个是字符串的结束标志0。
阅读(850) | 评论(0) | 转发(0) |