C陷阱与缺陷:第一,二章

第一章 词法“陷阱”


1. =
与 ==,在条件判断表达式时将”==”写成”=”这几乎是最容犯的一种错误,而且更为糟糕的是,编译器对此不会报错。产生这种错误的原因是,== 操作符左边是个左值,如果操作符两边都是右值就不会发生这种情况。因为,即使错将”==”写成了”=”,编译器将捕获这个语法错误(原因是”=”左边必须是左值)。一种解决方案是:将表达式中的左值放在操作符右边。如:

int a = 3;

if (3 = a) // 编译器将捕获到错误

    printf(“%d”, a);

 

 

2. 贪心法,编译器总是将一串操作符从左到右分解成最长的一个合法字符。如:

a---b // 相当于 (a--) – b

这里需要注意的一点是,符号中间不能有空白(空格符,制表符和换行符)。如:

a - -- b // 相当于a – (--b)

另外,还有 /* 与 / *的区别,/*是注释符,与*/组成一对,而/ *是两个独立的操作符,一般在以下情况需要注意:

y = x / *p // 不要写成 y = x /*p

为了更加清楚,最好写成:y = x / (*p)

 

 

3. 整形常量,如果第一个字符是数字0,那么该常量将被视作为八进制数。10 与010是两个截然不同的数。

 

 

4. 字符与字符串,‘’与 “” ,如果在””的地方写成了’’,一般编译器是会捕捉到语法错误的,原因是因为字符串是以数组形式存储的,它是一个字符指针,而字符是以整形值存储的。如果’’的地方写成了””,在赋值语句中这样的错误是未知的。

值得注意的是,在双引号括起来的字符串中,注释符/*属于字符串的一部分;而在注释中现出的双引号” ”又是注释的一部分。

 

 

 

 

第二章 语法“陷阱”

1. (复杂)函数声明,如果像: float f(); 这样的函数声明,谁都能理解,但如果是: (*(void(*) ())0) (); 这样的函数声明都将会不寒而栗。而这里讨论的就是这类复杂函数声明。理解复杂函数声明可以借助复杂表达式来理解。

我们可以先把上面声明拆成左右两边,左边是 (*(void(*) ())0),右边是()。然后,再和 float f()比较下,左边的(*(void(*) ()) 0) 就相当于f,右边()相同。

先来分析 (*(void(*) ()) 0),从里往外解析,先是void(*) (),再和 float f()比较下, void(*) 相当于f,右边的()相同。得出:void(*) ()是一个返回值为void,参数为空的函数指针原型。

再看右边的0,在这里即表示0地址被强制转换成了返回值为空的函数指针。

然后,总结得出,(*(void(*) ())0) 是将0地址强制转换返回为空的函数后,指向某个函数。

当然,面对这种复杂函数声明通常会采用typedef与解决。

以上参考:http://hi.baidu.com/pizzapark/blog/item/f6f0533daf50eae63d6d97bf.html/cmtid/b4e72c737ba798128701b0e9

 

2. 运算符优先级问题

优先级最高的并不是真正意义上的运算符,如数组下标,函数调用和结构成员选择操作符。单目运算符仅次于前面的运算符,在所有真正意义上的运算中,它们的优先级最高。双目运算符低于单目运算符,而在双目运算符中算术运算符优先级最高,移位运算符次之,关系运算符再次之,接着是逻运算符,赋值运算符,最后是条件运算符。

任何两个逻辑运算符都具有不同的优先级。所有的按位运算符优先级要比顺序运算符的优先级高,每个“与”运算要比相应的“或”运算优先级要高。而按位异或(^)的优先级位于按位与(&)和按位或(|)之间。

所有运算符中,逗号运算优先级最低。
如果有时难以分清那个运算符优先级高或低,这里的一个策略是加括号来强制提高优先级以增强表达式的可读性。

 

3. 语句结束标志:分号(;)

如果在程序中(单独的起始位置)不小心多写了一个分号不会造成什么不良后果,但在if或while语句后例外。如果在if或while后紧跟着一个分号,那么与条件判断后的结果就没有任何关系了。如:

if (x[i] > big); // 多了个分号

    big = x[i]; // 此句与if的条件判断没有关系,不管条件是否满足都将被执行

以上的解决方案是,在if 后面紧跟一对大括号”{}”。

另外一种是缺少分号,通常编译器是能捕获这一错误。但如果是在return 语句后,结果将可能不是预期的,如:

if (n < 3)

    return // 缺少分号

logrec.date = x[0];

logrec.time = x[1];

logrec.code = x[2];

 

至于书中提到的如果在结构声明后忘记分号现代编译器将捕获这一错误(VC 6.0和 code:: blocks 10.0下都捕获这一错误)。

 

 

 

4. switch 语句,通常容易在case部分遗忘break语句(当然有时是刻意的省略)。

 

 

5. “悬挂”的else

else始终与最近未匹配的if相结合,如:

if (x == 0)

    if (y == 0) error();

else {                        // 将与if (y == 0) error(); 结合

    z = x + y;

    f(&z);

}

解决这一问题的策略是添加大括号{},将不需要匹配的if封装起来。如:

if (x == 0)

{

    if (y == 0) error();

}

else

{

    z = x + y;

    f(&z);

}

posted @ 2010-10-24 23:55  jeff_nie  阅读(236)  评论(0编辑  收藏  举报