C 语言缺陷与陷阱(笔记)
C 语言像一把雕刻刀,锋利,并且在技师手中非常有用。和任何锋利的工具一样,C 会伤到那些不能掌握它的人。本文介绍 C 语言伤害粗心的人的方法,以及如何避免伤害。
第一部分研究了当程序被划分为记号时会发生的typedef funcptr void (*)();指向返回 void 的函数的指针
(*(funcptr)0)();//调用地址为 0 处的子程序
运算符并不总是具有你所想象的优先级
绑定得最紧密的运算符并不是真正的运算符:下标、函数调用和结构选择。这些都与左边相关联。 接下来是一元运算符。它们具有真正的运算符中的最高优先级。由于函数调用比一元运算符绑定 得更紧密,你必须写(*p)()来调用 p 指向的函数;*p()表示 p 是一个返回一个指针的函数。转换是一元运算符,并且和其他一元运算符具有相同的优先级。一元运算符是右结合的,因此*p++ 表示*(p++),而不是(*p)++。
在接下来是真正的二元运算符。其中数学运算符具有最高的优先级,然后是移位运算符、关系运 算符、逻辑运算符、赋值运算符,最后是条件运算符。需要记住的两个重要的东西是:
所有的逻辑运算符具有比所有关系运算符都低的优先级。
移位运算符比关系运算符绑定得更紧密,但又不如数学运算符。
乘法、除法和求余具有相同的优先级,加法和减法具有相同的优先级,以及移位运算符具有相同 的优先级。
还有就是六个关系运算符并不具有相同的优先级:==和!=的优先级比其他关系运算符要低。
在逻辑运算符中,没有任何两个具有相同的优先级。按位运算符比所有顺序运算符绑定得都紧密, 每种与运算符都比相应的或运算符绑定得更紧密,并且按位异或(^)运算符介于按位与和按位或之间。
三元运算符的优先级比我们提到过的所有运算符的优先级都低。
这个例子还说明了赋值运算符具有比条件运算符更低的优先级是有意义的。另外,所有的复 合赋值运算符具有相同的优先级并且是自右至左结合的
具有最低优先级的是逗号运算符。赋值是另一种运算符,通常具有混合的优先级。
看看这些分号!
或者是一个空语句,无任何效果;或者编译器可能提出一个诊断消息,可以方便除去掉它。一个 重要的区别是在必须跟有一个语句的 if 和 while 语句中。另一个因分号引起巨大不同的地方是函数定义前面的结构声明的末尾,考虑下面的程序片段:
struct foo {
int x;
}
f() {
...
}
在紧挨着 f 的第一个}后面丢失了一个分号。它的效果是声明了一个函数 f,返回值类型是 struct foo,这个结构成了函数声明的一部分。如果这里出现了分号,则 f 将被定义为具有默认的整型
返回值[5。]
switch 语句
C 中的 case 标签是真正的标签:控制流程可以无限制地进入到一个 case 标签中。看看另一种形式,假设 C 程序段看起来更像 Pascal:
switch(color) {
case 1: printf ("red"); case 2: printf ("yellow"); case 3: printf ("blue");
}
并且假设 color 的值是 2。则该程序将打印 yellowblue,因为控制自然地转入到下一个 printf()的调用。
这既是 C 语言 switch 语句的优点又是它的弱点。说它是弱点,是因为很容易忘记一个 break 语句,从而导致程序出现隐晦的异常行为。说它是优点,是因为通过故意去掉break 语句,可以很容易实现其他方法难以实现的控制结构。尤其是在一个大型的 switch 语句中,我们经常发现对
一个 case 的处理可以简化其他一些特殊的处理。
函数调用
和其他程序设计语言不同,C 要求一个函数调用必须有一个参数列表,但可以没有参数。因此, 如果 f 是一个函数,
f();
就是对该函数进行调用的语句,而
f;
什么也不做。它会作为函数地址被求值,但不会调用它[6。]
悬挂 else 问题
一个 else 总是与其最近的 if 相关联。
连接
一个 C 程序可能有很多部分组成,它们被分别编译,并由一个通常称为连接器、连接编辑器或加载器的程序绑定到一起。由于编译器一次通常只能看到一个文件,因此它无法检测到需要程序 的多个源文件的内容才能发现的错误。
你必须自己检查外部类型
假设你有一个 C 程序,被划分为两个文件。其中一个包含如下声明:
int n;
而令一个包含如下声明:
long n;
这不是一个有效的 C 程序,因
C语言缺陷与陷阱(笔记) 来自淘豆网m.daumloan.com转载请标明出处.