1.1 = 不是 ==

大多数由Algol派生的编程语言,比如Pascal和Ada,都使用:=表示赋值,用==表示判等。而C语言,是用=表示赋值,用==表示判等。这确实更方便,因为赋值比判等操作频繁得多,这样短符号就会用得更多。除此之外,C把赋值作为一种运算符,这样多重赋值(比如a=b=c)就能很容易地写出来,而且赋值也能被嵌入更大的表达式中。

这种便利也导致了一个潜在的问题:一个人可能不经意地把一个判等写成了赋值。因此下面的语句,很明显本来想在x等于y时执行break语句:

  1. if (x=y)
  2. break;

实际上却变成了把y赋值给x然后判断这个值是不是非零。再看下面这个循环,本意是想在读取文件时跳过空格、tab符、换行符:

  1. while (c = ' ' || c == '\t' || c== '\n')
  2. c = getc(f);

这个循环在比较空格时错误地使用了=而不是==。因为=|| 优先级低,这个”比较”实际上把c赋值为下面整个表达式的值:

  1. ' ' || c == '\t' || c == '\n'

因为’ ‘的值是非零的,所以这个表达式实际上把c赋值为1并把c原本的值覆盖了。所以这个循环是把文件的每一个字符都跳过了。在读取完整个文件之后的操作取决于库的实现是否允许一个程序在到达文件尾之后继续读取。如果允许,这个循环将无穷无尽地进行下去。

一些C编译器试图通过对形如e1 = e2的条件表达式给出警告信息来帮助他们的用户。当对一个变量赋值并检查这个值是否是0的时候,可以通过将条件表达式表达地更具体来避免在这类编译器产生警告。换句话说,即把:

  1. if (x = y)
  2. foo();

写成:

  1. if ((x = y) != 0)
  2. foo();

这也同时使你的意图表达地更直接。我们会在2.2节(17页)讨论为什么要在x = y之外加小括号。

也有可能会产生另一种相反的混淆:

  1. if ((filedesc == open(argv[i], 0)) <0 )
  2. error();

其中的open函数运行时若检测到错误则返回-1,成功返回时返回0或一个正数。这段代码的目的是把open函数的结果存放在filedesc中同时检查open的运行是否成功。当然,第一个==应该是=。而像例子中这样,是先把open的返回值和filedesc比较,并检查比较结果是否小于0,结果当然不小于0:因为==的结果不是0就是1,总之不会是负数。这样error函数就不会被调用。一切都看起来很正常,但是filedesc的值并没有改变,也绝不会被赋值为open的返回值。一些编译器可能会对其中和 0 的比较给出警告,但你也不应该指望编译器。