第二章 信息的表示和处理
2.1.3 对强制类型转换访问和打印不同程序对象的字节表示的代码的理解
1 |
|
对于show_int(12345),show_int()函数接受的参数12345为int型,4B大小,在该函数内部调用show_bytes(),第一个参数会传入12345所在的存储地址(&x),并将该地址参数强制类型转换为byte_pointer型,这表明当执行show_bytes()内部的printf()时,不再以int型作为单位访问,而是以unsigned char为单位访问x所在存储单元中的数据,由于12345是以int型传入show_int()中的,所以会for循环四次访问x所在存储单元中的数据。也就是说,存储12345的x地址传入show_bytes()后仍旧是原地址,只不过在show_bytes()中访问时只能一个字节一个字节的进行访问,因为unsigned char类型大小为1B
对于show_pointer(pval),show_pointer中传入的是12345所在存储单元的地址pval,在其内部传入show_bytes()中的第一个参数为指针的地址,即pval变量自身的地址,而不是pval所指向的地址,所以show_bytes()中会按unsigned char类型输出pval变量所存储的数据,循环次数为该指针的大小
2.1.7 练习题2.11 两数互换问题
原文前面讨论了利用异或交换两数的方法,但是对于下面的函数,运行结果有bug
1 |
|
之所以数组的长度为2k+1时,第k+1个数为0,原因在于在最后一次for循环时,first和last均指向a[k],此时inplace_swap中的x和y指向了同一个地址, x = y。运行时第一个式子 y = x ^ x = 0,第二个式子 x = 0^0 = 0,第三个式子 y = 0^0=0。为了避免first和last指向同一个数,可以去掉 ‘=’。
2.3.1 练习题2.27 判断溢出
依据原文上面中的推导过程,当或时发生溢出,所以
1 | int uadd_ok(unsigned x,unsigned y) { |
2.3.2 练习题2.31 我没笑~
1 | /* WARNING: This code is buggy */ |
这题给人一种表面上的正确,但是实际是错误的。
由于 有符号整数溢出的行为 自然表现为模运算,所以:
当x+y不溢出时,函数返回正确结果
当x+y正溢出时,该数会截断为后32位,即sum = x+y-2^32^,所以
sum - x = x + y - 2^32^ - x = y - 2^32^ = ( y - 2^32^ ) % 2^32^ = y
sum - y = x + y - 2^32^ - y = x - 2^32^ = ( x - 2^32^ ) % 2^32^ = x
所以( sum - x == y ) && ( sum - y == x ) 恒成立
当x+y负溢出时,sum = x+y+2^32^,所以
sum - x = x + y + 2^32^ - x = y + 2^32^ = ( y + 2^32^ ) % 2^32^ = y
sum -y = x + y + 2^32^ - y = x + 2^32^ = ( x + 2^32^ ) % 2^32^ = x
也是恒成立的
所以上述代码无论是否溢出,都恒成立,所以有问题。
2.3.2 练习题2.32 tsub_ok溢出问题
有一个特殊情况需要特别讨论,即y = -2^31^时,因为-y=-2^31^
x>0时,不妨设x = 1,则x - y真值为1+2^31^,真值发生了溢出,但是在tadd_ok()中,
x>0,y<0,返回的是1即没有溢出。
x<0时,不妨设x = -1,则x - y真值为-1+2^31^,真值没有溢出,但是在tadd_ok()中,
x<0,y<0,x+y = [1111……1111] + [1000……0000]="[0111……1111]">0,所以返回的是0即发生了溢出0,y<0,x+y>
所以需要在tsub_ok()单独处理这种情况
1 | int tsub_ok(int x, int y) { |