读谭师傅的书(10) The C Book - 译者序
Apr 02

前面已经学习到第3.8节。现在继续学习第3.9节,《赋值运算符和赋值表达式》。

1、谭师傅开宗明义地说,“赋值符号‘=’就是赋值运算符”。由于中文里面的“是”字比较含糊,也没有单复数,学生学到这里有可能会以为编译器会对“=”和“+=”这两种运算符做不同处理。事实上,从后面也可以看到,谭师傅始终也没讲清楚这两种情况的相同和不同之处。

2、谭师傅在讲到类型转换的时候说,将浮点型数据转换成整型时,会“舍弃浮点数的小数部分”。这又是一个谭氏标准,因为ANSI C里规定既可以向上取整,也可以向下取整,由编译器自己决定。

3、 谭师傅还说,“将一个double型数据赋给一个float变量时,截取其前面7位有效数字”。float的有效数字不一定是7位,这点我们之前已经提到 过了。这里要说的是,标准C规定这种情况下既可以向上取最近的数值,也可以向下取最近的数值,与前面第2点类似,也是由编译器决定的。

4、谭师傅说,在进行以上转换时(即double到float),“数值范围不能溢出”,否则“就出现溢出的错误”。不知道所谓“溢出的错误”是指的什么?不过标准C里倒是有说法,说这种情况是undefined,也就是说任何事都有可能发生。

5、 谭师傅说,“将一个int, short, long型数据赋给一个char型变量时,只将其低8位原封不动地送到char型变量(即截断)”。同样,如果系统的char是有符号的,而且数据太大导 致溢出,标准C里也是undefined,任何事情都有可能发生。事实上,如果想把一个int数据(比方说“int i”)的最低8位拿出来放到一个字符型变量里,唯一可靠的方法是“unsigned char c = i & 0xff;”。后面讲long转换成int也类似。

6、谭师傅在总结类型转换时说,“赋值规则…归根到底就一条:按存储单元中的存储 形式直接传送”。看来以上谭师傅的种种高论基本上就归根到这一条了。其实谭师傅自己也讲过,从比较小的类型转换成比较大的类型时,标准C规定的是数值传 递,所以在用补码存储数据的系统中,负数前面会补1,所谓“符号扩展”是也。这会怎么就忘记了呢?更重要的是,标准C中规定,如果有符号的类型数据溢出, 其结果是undefined,也就是说无法预料。如果写程序时依赖于“存储形式直接传送”,其结果就算不出错,也必然依赖于特定系统、不可移植。

7、谭师傅在第3小节《复合的赋值运算符》中给出了几个复合运算符(如+=)的例子,并且教导我们说,a+=3等价于a=a+3,并在后面又重复了一次这 个“等价”关系。很可惜的是,对于a+=3和a=a+3的区别,谭师傅只字未提。其实说起来也很简单,就是a+=3时,a的值只会取一次,而在a=a+3 中,a的值会被取两次。如果a是一个变量,那么这两种情况确实没区别,但如果a里面有一个表达式呢?课后思考题:a[i++]+=3 和 a[i++] = a[i++] + 3等价吗?

8、谭师傅还教导我们说,使用这种复合运算符的目的之一,是为了“提高编译效率”,因为“这样的写法与‘逆波兰’式一致”。看到这句,我又被谭师傅雷到 了。我咋从来就不知道逆波兰式是这样的哩?让我想想,a+3的逆波兰式是a 3 +,如果把=也勉强算上,那就应该是a a 3 + =。啊,有个“+=”吔。

9、谭师傅在第4小节《赋值表达式》中说,“常变量也不能作为左值,因为常变量不能被赋值”。看来谭师傅不雷死人不算完。不知道一个不能被赋值的变量要来 有什么用?读者可能会说了,谭师傅可能只是一时笔误,其实他的“真实意思”是指常变量只能在声明的时候同时赋值,而之后就不能再被赋值了。嗯,这个解释可 以接受。不过且慢,谭师傅书写到这里还根本没讲过什么是常变量哩。而且,下面谭师傅给出的例子里就有一个“int a=3,b,c;” ,说明谭师傅也认为声明的同时赋值也算是赋值的。所以谭师傅的“真实意思”只可能是,声明并定义常变量时,那个语句不算赋值,其它都算。我的天!

10、谭师傅接着又花了很大的篇幅讲了这么一个道理:“赋值表达式中的‘表达式’,又可以是一个赋值表达式“。本来嘛,像“a=b=5”这样的式子在实际 编程时是应该尽量避免的。不过讲讲特殊情况也好,免得看同学(注:同样学谭师傅的书的同学)的代码看不懂。不过呢,这里谭师傅真的是雷死人不偿命,一口气 上来俩雷人表达式。第一个是 (a=3*5)=4*3。据说谭师傅的书第三版和第二版之间的区别之一就是第三版用了TC++ 3.0。我算是看出来了,这是把C++的句法当C来讲了。第二个雷人表达式是 a+=a-=a*a。本来我想谭师傅的意思是要讲讲运算符的运行顺序,不过一不小心就炸了雷了。在C语言里,如果在同一个语句中有多个表达式,其中有side-effect(如赋值),而且其结果取决于这些表达式的执行顺序,那么其结果将是unspecified,也就是实际结果取决于编译器。K&R第二版的2.12 节中还给出了几个类似的错误,如 a[i] = i++; 和 printf(”%d %d”, ++n, power(2,n)); 。

既然已经学习到了这里,就顺便学习一下第3.10节,《逗号运算符和逗号表达式》。这个逗号操作符有“最难懂的操作符”(most obscure operator)的称号。谭师傅讲了一个小节,总之就是教你怎么把程序写得更难懂。不过我还是认真学习了,免得看同学(注:同上)的代码看不懂。

好了,第三章就学习完了。至此,我能够从google books上看到的内容已经学习完毕。

“到这里,就到这里。休息,休息一下。”

休息好了再继续

“读谭师傅的书(11)”有8篇评论

  1. anbangli Says:

    觉得这个系列非常好,很有意义,读的时候也学到不少东西。希望能够继续下去……



    多谢。现在是找不到第三版的书。相对第二版而言谭师傅的第三版还是有不少进步的 :-)

  2. fuzzify Says:

    哈哈哈,工程师读谭师傅的书读成一休师傅了。

  3. ronaldfree Says:

    一定要继续。你的系列也有一些地方看不懂,有很多地方只是一笔带过,下次能不能说的详细点?我是菜鸟,请原谅

  4. anbangli Says:

    在这里可以下载到谭氏第2版的PDF完整版:
    http://d.download.csdn.net/down/628646/zhoujie84910209
    如果下载不了,可以发信给我的邮箱里,然后由我寄给您。
    总之,衷心地希望您能继续这个系列,对谭师傅进行一个完整的评论,让更多的被谭师傅领入岐途的学生明白过来。

  5. anbangli Says:

    http://d.download.csdn.net/down/628646/zhoujie84910209

  6. anbangli Says:

    上面那个网址可以下载到第2版的完整PDF版。希望这个系列能够继续下去。



    谢谢。第二版的其实以前我也找到过。后来发现第三版对于第二版的修改还是不少的。再拿第二版来挑错有点不太地道。不过有时间的话我还是会考虑继续写下去的。

  7. anbangli Says:

    在网上找不到第3版的PDF。
    下面这个网址声称是第3版,但是与我手头的第3版书本不同,象是某人自己写的笔记。
    http://d.download.csdn.net/down/592299/daxiishuca
    可惜我没有扫描仪,不能把手头的第3版书扫描出来。

  8. 穆扬 Says:

    2.
    这一条博主的记忆似乎有误,
    C89或C99都是
    When a finite value of real floating type is converted to an integer type other than _Bool,
    the fractional part is discarded (i.e., the value is truncated toward zero).

发表评论

CAPTCHA Image
*