文档介绍:1. 介绍: 3
= 不是= = 4
多字符的标记符 5
字符串和字符 6
2 语法陷阱 6
理解声明 6
操作符不具有你所期望的优先级 8
分号的用法 10
Switch 语句 11
函数调用 12
else 语句问题 12
3 链接 13
你必须自己检查外部类型定义 13
4、语义陷阱 14
表达式的运算顺序 15
逻辑操作符&& 、|| 、! 15
下标从 0 开始 16
C 语言并不总是对实参进行匹配 17
、指针不是数组 18
避免使用提喻法 18
空指针并不是空字符串 19
整型溢出 19
移位操作符 20
5 库函数 20
getc返回一个整数 20
缓冲输出和内存分配 21
宏不是函数 22
宏并非类型定义 24
7 可移植性的缺陷 24
如何命名 24
一个整数有多大? 25
字符是有符号还是无符号? 26
合适的移位是带符号还是不带符号呢? 26
除法怎样进行? 26
一个随机数有多大 27
大小写转换 27
先释放, 再重新分配 28
一个可移植性问题的例子 29
8 可利用的资源 31
C语言陷阱
Andrew Koenig
AT&T 贝尔实验室
Murray Hill ,新泽西 07974
摘要
C 语言就象一把雕刻用刀---简单,锋利,在熟手中极其有用。但象所有锋利的工具一样,C语言会伤害到那些不知驾驭的人。本文将展示C是如何伤害那些粗心的人,并将介绍如何避免这种伤害。
0. 介绍:
对于专家来说,C 语言易于使用和实现。C语言简洁而富于表现力。很少有约束来防止程序员犯一些粗心的错误。人们所得到的错误结果及其原因之间常常没有明显的联系。
在这份文献中,我们将看到一些不可预知的结果。因其不可预知性,我们无法对其做出完备的分类。但通过监视运行C程序时那些必然发生的事情,我们竭尽全力这样做了。我们假定读者要对C语言有一定的熟悉程度。
第一章的问题是当把C程序分解为标记符时所遇到的。第二章跟踪当编译器将C的标记符分为申明,表达式和语句时的问题。第三章确认C程序由几个单独编译的部分组成。第四章提示一些错误概念:即程序还在运行时发生的状况。第五章检查程序及其调用的库函数之间的关系。第六章提醒我们所写的代码和实际运行的并不一定一致; 预处理程序已经先一步获得代码并做出处理。最后,第七章我们探讨可移植性问题,看哪些因素导致程序在一个平台可以正常运行而在其他平台上却得不到所要的结果。
1. 词法陷阱
编译器的第一个部分通常被称为词法分析器。词法分析器将程序看成一系列字符并将其分解为标记符。标记符由一个或多个在被编译程序中具有相对统一意思的字符组成。例如,在
C 语言中,标识符“-> ”与组成他的两个字符之间有着异常明显的区别,且独立于上下文。
另举一个例子,考虑以下表述:
if(x 〉big)big = x ;
在这个表述中,除关键词 if 和标识符big的两个实例之外,每个非空的字符都是一个独立的标识符。
实际上C 语言分两步被分解为标记符。首先经过预处理程序。它必须将程序标识符化以找到定义,有些定义还以宏的形式体现。接着对宏求值并进行替换。最后,宏替换的结果再编译形成编译器专用的字符流。编译器接着第二次将该字符流分解为标记符。
在这一章节中我们将浏览常见的一些对标记符错误理解以及标记符与组成它的字符之间的区别。最后将我们谈谈预处理的问题。
= 不是= =
起源于Algol的编程语言如Pascal和Ada用“:=”表示赋值,用“=”表示比较。然而,C使用“=”表示赋值,用“= =”表示比较。这是因为赋值使用的频率更多,所以将它用较短的符号表示。
此外,C 把赋值看成是一种操作,故可以很容易的写出如“a=b=c”之类的多重赋值,且赋值能够包含到非常复杂的表达式中。
这种便利导致一些潜在的问题:人们可能不经意的于计划比较处进行赋值操作。
比如以下表达式,看起来好像是在比较 x 和 y 是否相等:<非0-1表示真,0表示假)
if( x = y )
foo();
实际上却是将 y 的值赋给 x ,并判断该值是否非 0 。看一下下面的循环,它试图在一个文件中跳过空格,TAB,换行符:
while( c = = ' ' || c = '\t' || c = ='\n')
c= getc(f);
程序员在比