分类: C/C++
2012-02-13 13:27:33
——《C Traps and Pitfalls》(Addison-Wesley, 1989, ISBN 0-201-17928-8)noted by leo
2.13.2012
Andrew Koenig
AT&T贝尔实验室
Murray Hill ,新泽西07974
C语言就象一把雕刻用刀---简单 ,锋利,在熟手中极其有用。但象所有锋利的工具一样,C语言会伤害到那些不知驾驭的人。本文将展示C是如何伤害那些粗心的人,并将介绍如何避免这种伤害。
C的参考手册描述了如何进行判决:“如果(编译器)输入流在截止到某个字符之前都已经被分解为一个个的符号,则下一个符号将包含从该字符之后可能组成一个符号的最长字符串。”
a---b 与 a-- -b的含义相同,而与a- --b的含义不同。
假如 / 是某个记号的第一个字符,且其后紧跟 * ,则不管上下文如何,这两个字符都表示注释的开始,下面的表述看起来象是设定y的值为x除以指针p所指向的值:
y = x/*p /* p指向除数*/;
实际上,由于/* 表示注释开始,编译器会简单的忽略代码直至*/的出现。换句话说,上面的表达式实际上仅仅将x的值赋给y ,甚至都没有引用p,如果后面没有*/则编译报错。
重写上述表达式为:
y = x/ *p; /*p points to the divisor */ (注意:/和*之间有空格隔开!)
或者:
y = x/(*p); /* p points to the divisor*/;
都会按照注释中的意思进行除法运算。
复合赋值操作如 + = 是真正的多重记号。即,
a + /* strange */ = 1;
表示的意思同以下:
a + = 1;
这些操作符是唯一的,只有他们才可能看起来是单独的记号,而实际上却是多重记号。
(leo: VC6.0下并不能编译通过+ =)
C语言中的单引号和双引号的含义迥异,某些情况下如果弄混,编译器并不报错,从而产生难以预料的结果。
字符使用单引号,它只是整数的另一种写法。该整数的值为在字符集中与字符相应的序列值。因此,在ASCII序列中’a’表示0141或者97。
字符串使用双引号,提供一种简洁的写法,将一个指针指向一个未命名的数组,该数组被初始化为双引号之间的字符和一个额外的二进制值为0的字符’\0’。
printf("hello\n");
char hello[] = {'h','e','l','l','o','\n',0}; //注意后面的0!
printf(hello);
两者是等效的。
因为单引号括起的一个字符代表一个整数,而用双引号括起的一个字符代表一个指针,所以两者混用时,编译器的类型检查功能会检测到这样的错误。
char *slash = '/';
编译错误,’/’不是一个字符指针。
对于printf('\n');这样的错误,有的编译器保存,有的不报错。
因为整型数(一般16位或32位)通常都足够大,可以同时表示几个字符(一般8位),所以有的 C 编译器允许字符常量(以及字符串常量)中有多个字符(若整数32位,字符8位,则单引号内可包含4个字符)。这表明当用‘yes’替代“yes”时可能不会被发现。后者表示四个分别装有y,e,s,\0字符的连续地址空间的首地址。前者表示一个由y,e,s以某种方式定义的字符的整数。在这两个变量之间如果有相似之处都只是一种巧合。
Borland C++v5.5 和LCC v3.6中,最后的整数值即第一个字符的整数值,VC6.0和GCC v2.95中为最后一个字符的值。
a+++++b 编译报错,因为a++的值为右值,a++ ++ 是错误的,右值不可++。