练习题答案
2.1 在我们开始查看机器级程序的时候, 理解十六进制和二进制格式之间的关系将是很重要的。虽然本书中介绍了完成这些转换的方法, 但是做点练习能够让你更加熟练。
A. 将 0 x 39 A 7 F 8 转换成二进制:
十六进制 3 9 A 7 F 8
二进制 0011 1001 1010 0111 1111 1000
B. 将二进制 1100100101111011 转换成十六进制:
二进制 1100 1001 0111 1011
十六进制 C 9 7 B
C. 将 0 xD 5 E 4 C 转换成二进制:
十六进制 D 5 E 4 C
二进制 1101 0101 1110 0100 1100
D. 将二进制 1001101110011110110101 转换成十六进制:
二进制 10 0110 1110 0111 1011 0101
十六进制 2 6 E 7 B 5
2.2 这个问题给你一个机会思考 2 的幂和它们的十六进制表示。
$n$ | ${2}^{n}$ (十进制) | ${2}^{n}$ (十六进制) |
9 | 512 | $0 \times {200}$ |
19 | 524 288 | 0 x 80000 |
14 | 16 384 | $0 \times {4000}$ |
16 | 65 536 | 0 x 10000 |
17 | 131 072 | 0 x 20000 |
5 | 32 | $0 \times {20}$ |
7 | 128 | $0 \times {80}$ |
2.3 这个问题给你一个机会试着对一些小的数在十六进制和十进制表示之间进行转换。对于较大的数, 使用计算器或者转换程序会更加方便和可靠。
十进制 | 二. 进制 | 十六进制 |
0 | 0000 0000 | $0 \times {00}$ |
${167} = {10} \cdot {16} + 7$ | 1010 0111 | 0 xA 7 |
${62} = 3 \cdot {16} + {14}$ | 0011 1110 | 0 x 3 E |
${188} = {11} \cdot {16} + {12}$ | 1011 1100 | 0 xBC |
$3 \cdot {16} + 7 = {55}$ | 0011 0111 | $0 \times {37}$ |
$8 \cdot {16} + 8 = {136}$ | 1000 1000 | $0 \times {88}$ |
${15} \cdot {16} + 3 = {243}$ | 1111 0011 | 0 xF 3 |
$5 \cdot {16} + 2 = {82}$ | 0101 0010 | 0 x 52 |
${10} \cdot {16} + {12} = {172}$ | 1010 1100 | 0 xAC |
${14} \cdot {16} + 7 = {231}$ | 1110 0111 | 0 xE 7 |
2.4 当开始调试机器级程序时, 你将发现在许多情况中, 一些简单的十六进制运算是很有用的。可以总是把数转换成十进制, 完成运算, 再把它们转换回来, 但是能够直接用十六进制工作更加有效, 而且能够提供更多的信息。
A. \(0 \times {503}\mathrm{c} + 0 \times 8 = 0 \times {5044}\) 。 8 加上十六进制 \(\mathrm{c}\) 得到 4 并且进位 1 。
B. $0 \times {503}\mathrm{c} - 0 \times {40} = 0 \times 4\mathrm {{ffc}} $ 。在第二个数位, 3 减去 4 要从第三位借 1 。因为第三位是 0 ,所以我们必须从第四位借位。
C. \(0 \times {503}\mathrm{c} + {64} = 0 \times {507}\mathrm{c}\) 。十进制 \({64}\left( {2}^{6}\right)\) 等于十六进制 \(0 \times {40}\) 。
D. 0 x 50 ea-0 x 503 c=0 xae。十六进制数 a(十进制 10) 减去十六进制数 c(十进制 12),我们从第二位借 16,得到十六进制数 \(\mathrm{e}\) (十进制数 14)。在第二个数位,我们现在用十六进制 \(\mathrm{d}\) (十进制 13) 减去 3,得到十六进制 a(十进制 10)。
2.5 这个练习测试你对数据的字节表示和两种不同字节顺序的理解。
小端法: 21 大端法: 87
小端法 :2143 大端法: 87 65
小端法 : 21 43 65 大端法: 87 65 43
回想一下,show bytes 列举了一系列字节,从低位地址的字节开始,然后逐一列出高位地址的字
节。在小端法机器上, 它将按照从最低有效字节到最高有效字节的顺序列出字节。在大端法机器上, 它将按照从最高有效字节到最低有效字节的顺序列出字节。
2.6 这又是一个练习从十六进制到二进制转换的机会。同时也让你思考整数和浮点表示。我们将在本章后面更加详细地研究这些表示。
A. 利用书中示例的符号, 我们将两个串写成:
$\begin{array}{llllllll} 0 & 0 & 3 & 5 & 9 & 1 & 4 & 1 \end{array}$
00000000001101011001000101000001 ****** \(\begin{array}{llllllll} 4 & A & 5 & 6 & 4 & 5 & 0 & 4 \end{array}\) 01001010010101100100010100000100
B. 将第二个字相对于第一个字向右移动 2 位, 我们发现一个有 21 个匹配位的序列。
C. 我们发现除了最高有效位 1 , 整数的所有位都嵌在浮点数中。这正好也是书中示例的情况。另外, 浮点数有一些非零的高位不与整数中的高位相匹配。
2.7 它打印 61 62 63 64 65 66。回想一下,库函数 strlen 不计算终止的空字符,所以 show_ bytes 只打印到字符‘f’。
2.8 这是一个帮助你更加熟悉布尔运算的练习。
运算 | 结果 | 运算 | 结果 |
$a$ | [01101001] | $a\& b$ | [01000001] |
$b$ | [01010101] | $a \mid b$ | [01111101] |
$\sim a$ | [10010110] | $a \land b$ | [00111100] |
$\sim b$ | [1010101] |
2.9 这个问题说明了怎样用布尔代数来描述和解释现实世界的系统。我们能够看到这个颜色代数和长度为 3 的位向量上的布尔代数是一样的。
A. 颜色的取补是通过对 \(R\) 、 \(G\) 和 \(B\) 的值取补得到的。由此,我们可以看出,白色是黑色的补,黄色是蓝色的补,红紫色是绿色的补,蓝绿色是红色的补。
B. 我们基于颜色的位向量表示来进行布尔运算。据此, 我们得到以下结果:
蓝色(001) 绿色(010) = 蓝绿色(011)
黄色( 110 ) & 蓝绿色( 011 )= 绿色( 010 )
红色(100) ^ 紫红色(101)= 蓝色(001)
2.10 这个程序依赖于两个事实,EXCLUSIVE-OR 是可交换的和可结合,以及对于任意的 \(a\) ,有 \(a \land a = 0\) 。
步骤 | $* x$ | *y |
初始 | $a$ | $b$ |
步骤 1 | $a$ | ${a}^{ \land }b$ |
步骤 2 | ${a}^{ \land }\left( {{a}^{ \land }b}\right) = \left( {{a}^{ \land }a}\right) {}^{ \land }b = b$ | ${a}^{ \land }b$ |
步骤 3 | $b$ | ${b}^{ \land }\left( {a \land b}\right) = \left( {b \land b}\right) \land a = a$ |
某种情况下这个函数会失败,参见练习题 2.11 。
2.11 这个题目说明了我们的原地交换例程微妙而有趣的特性。
A. first 和 last 的值都为 \(k\) ,所以我们试图交换正中间的元素和它自己。
B. 在这种情况中,inplace_swap 的参数 \(x\) 和 \(y\) 都指向同一个位置。当计算 \({x}^{\lambda ,x}y\) 的时候,我们得到 0 。然后将 0 作为数组正中间的元素, 而后面的步骤一直都把这个元素设置为 0 。我们可以看到,练习题 2.10 的推理隐含地假设 \(\mathrm{x}\) 和 \(\mathrm{y}\) 代表不同的位置。
C. 将 reverse_array 的第 4 行的测试简单地替换成 first<last, 因为没有必要交换正中间的元素和它自己。
2.12 这些表达式如下:
A. \(x\& {0 xFF}\)
B. \({x}^{2} \sim 0{x}^{5}{F}^{2}\)
C. $\mathrm{x} \mid 0\mathrm {{xFF}} $
这些表达式是在执行低级位运算中经常发现的典型类型。表达式 $\sim 0\mathrm {{xFF}} $ 创建一个掩码,该掩码 8 个最低位等于 0 ,而其余的位为 1 。可以观察到,这些掩码的产生和字长无关。而相比之下,表达式 0 xFFFFFF 00 只能工作在 32 位的机器上。
2.13 这个问题帮助你思考布尔运算和程序员应用掩码运算的典型方式之间的关系。代码如下:
/ Declarations of functions implementing operations bis and bic / int bis( int x, int m ) ; int bic(int x, int m); / Compute xly using only calls to functions bis and bic / int bool_or(int x, int y) { int result = bis(x,y); return result; } / Compute x "y using only calls to functions bis and bic / int bool_xor( int x, int y ) { int result = bis(bic(x,y),bic(y,x)); return result; }
bis 运算等价于布尔 $\mathrm {{OR}} $ ——如果 \(\mathrm{x}\) 中或者 \(\mathrm{m}\) 中的这一位置位了,那么 \(\mathrm{z}\) 中的这一位就置位。另一方面,bic(x,m)等价于 \(\mathrm{x}\varepsilon \sim \mathrm{m}\) ;我们想实现只有当 \(\mathrm{x}\) 对应的位为 1 且 \(\mathrm{m}\) 对应的位为 0 时,该位等于 1。
由此, 可以通过对 bis 的一次调用来实现。为了实现, 我们利用以下属性
2.14 这个问题突出了位级布尔运算和 \(\mathrm{C}\) 语言中的逻辑运算之间的关系。常见的编程错误是在想用逻辑运算的时候用了位级运算, 或者反过来。
表达式 | 值 | 表达式 | 值 |
$\mathrm{x}$ & $\mathrm{y}$ | $0 \times {20}$ | x && y | $0 \times {01}$ |
$x + y$ | 0 x 7 F | x Il y | $0 \times {01}$ |
~x 1 ~y | 0 xDF | !x || !y | $0 \times {00}$ |
x & !y | $0 \times {00}$ | x && ~y | $0 \times {01}$ |
2.15 这个表达式是 \(\left( {\mathrm{x} \land \mathrm{y}}\right)\) 。
也就是,当且仅当 \(\mathrm{x}\) 的每一位和 \(\mathrm{y}\) 相应的每一位匹配时, \(\mathrm{x}\lambda \mathrm{y}\) 等于零。然后,我们利用! 来判定一个字是否包含任何非零位。
没有任何实际的理由要去使用这个表达式,因为可以简单地写成 \(\mathrm{x} = = \mathrm{y}\) ,但是它说明了位级运算和逻辑运算之间的一些细微差别。
2.16 这个练习可以帮助你理解各种移位运算。
$x$ | $x < < 3$ | (逻辑) $x > > 2$ | (算术) $x > > 2$ | ||||
十六进制 | 二进制 | 二进制 | 十六进制 | 二进制 | 十六进制 | 二进制 | 十六进制 |
0 xC 3 | [11000011] | [00011000] | 0 x 18 | [00110000] | $0 \times {30}$ | [11110000] | 0 xF 0 |
0 x 75 | [01110101] | [10101000] | 0 xA 8 | [00011101] | 0 x 1 D | [00011101] | 0 x 1 D |
0 x 87 | [10000111] | [00111000] | 0 x 38 | [00100001] | 0 x 21 | [11100001] | 0 xE 1 |
0 x 66 | [01100110] | [00110000] | 0 x 30 | [00011001] | 0 x 19 | [00011001] | 0 x 19 |