第二章 信息的表示和处理
¶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 |
|
之所以数组的长度为2*k+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 判断溢出
依据原文上面中的推导过程,当$$s<x$$或$$s<y$$时发生溢出,所以
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-232,所以
sum - x = x + y - 232 - x = y - 232 = ( y - 232 ) % 232 = y
sum - y = x + y - 232 - y = x - 232 = ( x - 232 ) % 232 = x
所以( sum - x == y ) && ( sum - y == x ) 恒成立
-
当x+y负溢出时,sum = x+y+232,所以
sum - x = x + y + 232 - x = y + 232 = ( y + 232 ) % 232 = y
sum -y = x + y + 232 - y = x + 232 = ( x + 232 ) % 232 = x
也是恒成立的
所以上述代码无论是否溢出,都恒成立,所以有问题。
¶2.3.2 练习题2.32 tsub_ok溢出问题
有一个特殊情况需要特别讨论,即y = -231时,因为-y=-231
-
x>0时,不妨设x = 1,则x - y真值为1+231,真值发生了溢出,但是在tadd_ok()中,
x>0,y<0,返回的是1即没有溢出。
-
x<0时,不妨设x = -1,则x - y真值为-1+231,真值没有溢出,但是在tadd_ok()中,
x<0,y<0,x+y = [1111……1111] + [1000……0000] = [0111……1111] >0,所以返回的是0即发生了溢出
所以需要在tsub_ok()单独处理这种情况
1 | int tsub_ok(int x, int y) { |