本节书摘来自异步社区《C语言解惑》一书中的第1章,第1.4节,作者 傅道坤,更多章节内容可以访问云栖社区“异步社区”公众号查看
谜题1.4 二进制位操作符请问,下面这个程序的输出是什么?
define PRINT(int) printf(#int " = %d\n", int) main() { int x, y, z; x = 03; y = 02; z = 01; PRINT( x | Y & z ); (1.4.1) PRINT( x | y & ~ z ); (1.4.2) PRINT( x ^ y & ~ z ); (1.4.3) PRINT( x & y && z ); (1.4.4) x = 1; y = -1; PRINT( ! x | x ); (1.4.5) PRINT( ~ x | x ); (1.4.6) PRINT( x ^ x ); (1.4.7) x <<= 3; PRINT(x); (1.4.8) y <<= 3; PRINT(y); (1.4.9) y >>= 3; PRINT(y); (1.4.10) }输出:
x | y & z = 3 (1.4.1) x | y & ~ z = 3 (1.4.2) x ^ y & ~ z = 1 (1.4.3) x & y && z = 1 (1.4.4) ! x | x = 1 (1.4.5) ~ x | x = -1 (1.4.6) x ^ x = 0 (1.4.7) x = 8 (1.4.8) y = -8 (1.4.9) y = ? (1.4.10)解惑1.4 二进制位操作符
初始值:x = 03, y = 02, z = 01
x | y & z 在C语言里,以零(0)开头的整数常数是八进制数值。因为八进制很容易转换为二进制,所以八进制数值很适合用来进行各种二进制位操作。在这道谜题里,01、02、03(八进制)与1、2、3(十进制)完全等价,而我们在此明确地使用八进制数值的目的是,为了提醒读者注意这道谜题是对变量x、y和z的值进行位操作。
(x | (y & z)) 对操作符和操作数进行绑定。
(x | (02 & 01)) 先对最内层的表达式求值。
(x | 0) 在二进制里,01=1,02=10,03=11
10 & 01
00
(03 | 0)
03
00
| 11
11
PRINT宏:这个程序里的“PRINT”宏需要用到C语言预处理器的“#”操作符和字符串合并操作。这个“PRINT”宏有一个形式参数int。在扩展的时候,形式参数int将被替换为宏调用里的实际参数。在形式参数的前面加上一个“#”字符作为前缀,将使得实际参数被括在一对双引号里。于是
PRINT (x | y & z)
将被扩展为:
printf ("x | y & z" " = %dn", x | y & z)
C语言预处理器会自动将相邻的字符串合并,所以这又等价于:
printf ("x | y & z = %dn", x | y & z)
初始值:x=03,y=02,z=01 x | y & ~ z
(x | (y & ( ~ z)))
(x | (y & ~ 01)) “~”操作符对它的操作数按位求补,所以“0...01”将变成“1...10”。
(x | (02 & ~ 01))
(03 | 02) 在二进制里,
0...010
& 1...110
0000010
3
10
| 11
11
初始值:x = 03, y = 02, z = 01
x ^ y &;~z
(x ^ (y & (~z))) 这道谜题与前一题大同小异,只是把逻辑或操作符(|)换成了异或操作符(^)而已。
(x ^ (02 &~01))
(03 ^ 02)
1 在二进制里,
10
^ 11
01
初始值:x = 03, y = 02, z = 01
x & y && z
((x & y) && z)
((03 & 02) && z)
(02 && z)
(TRUE && z)
(TRUE && 01)
(TRUE && TRUE)
TRUE, 即1 只有当两个操作数都是TRUE时,“&&”操作的结果才会是TRUE。
初始值:x = 01
! x | x
((! x) | x)
((! TRUE) | x)
(FALSE | 01)
(0 | 01)
01
初始值:x = 01
~x | x
((~x)|x)
(~01|01)
-1 在二进制里,
1...110
| 0...001
1...111,即-1
(不管x的值是什么,这道谜题的答案都是相同的。实际上,在使用二进制补码来表示负数的平台(如Intel 8086系列或Motorola 68000系列)上,这道谜题的答案永远是-1;在使用1的补码来表示负数的平台上,这道谜题的答案将是-0。在这本书里,凡是涉及具体机器的谜题都使用二进制补码来表示负数。)
初始值:x = 01
x ^ x
(01 ^ 01)
0 在二进制里,
0...01
^ 0...01
0...00
(不管x的值是什么,这道谜题的答案都是相同的)
初始值:x = 01
x <<= 3
x = 01 << 3
x = 8 在二进制里,
0000...01
3
1...111,也就是8。
从效果上看,每左移一位就相当于乘以2。
初始值:y = -01
y << = 3
y = -01 << 3
y = -8 在二进制里,
1111...11
<< 3
1...11000,也就是-8。
初始值:y = -08
y >> = 3
y = -08 >> 3
这道谜题的答案似乎显而易见:y=-1,可惜情况并非总是如此——有些计算机在进行右移操作时不保留操作数的符号位,而且C语言本身也不保证移位操作的结果在数学意义上肯定是正确的。不管怎样,还是使用更为清晰的除以8的表达方式,即“y/8”。
相关资源:敏捷开发V1.0.pptx