《C语言解惑》一1.4 二进制位操作符

    xiaoxiao2024-06-05  100

    本节书摘来自异步社区《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 二进制位操作符

    1.4.1

    初始值: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)

    1.4.2

    初始值: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

    1.4.3

    初始值:x = 03, y = 02, z = 01

    x ^ y &;~z

    (x ^ (y & (~z)))   这道谜题与前一题大同小异,只是把逻辑或操作符(|)换成了异或操作符(^)而已。

    (x ^ (02 &~01))

    (03 ^ 02)

    1   在二进制里,

            10

          ^ 11

           01

    1.4.4

    初始值: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。

    1.4.5

    初始值:x = 01

    ! x | x

    ((! x) | x)

    ((! TRUE) | x)

    (FALSE | 01)

    (0 | 01)

    01

    1.4.6

    初始值:x = 01

    ~x | x

    ((~x)|x)

    (~01|01)

    -1   在二进制里,

           1...110

          | 0...001

           1...111,即-1

    (不管x的值是什么,这道谜题的答案都是相同的。实际上,在使用二进制补码来表示负数的平台(如Intel 8086系列或Motorola 68000系列)上,这道谜题的答案永远是-1;在使用1的补码来表示负数的平台上,这道谜题的答案将是-0。在这本书里,凡是涉及具体机器的谜题都使用二进制补码来表示负数。)

    1.4.7

    初始值:x = 01

    x ^ x

    (01 ^ 01)

    0   在二进制里,

           0...01

          ^ 0...01

           0...00

    (不管x的值是什么,这道谜题的答案都是相同的)

    1.4.8

    初始值:x = 01

    x <<= 3

    x = 01 << 3

    x = 8   在二进制里,

           0000...01

               3

            1...111,也就是8。

    从效果上看,每左移一位就相当于乘以2。

    1.4.9

    初始值:y = -01

    y << = 3

    y = -01 << 3

    y = -8   在二进制里,

           1111...11

          <<     3

           1...11000,也就是-8。

    1.4.10

    初始值:y = -08

    y >> = 3

    y = -08 >> 3

    这道谜题的答案似乎显而易见:y=-1,可惜情况并非总是如此——有些计算机在进行右移操作时不保留操作数的符号位,而且C语言本身也不保证移位操作的结果在数学意义上肯定是正确的。不管怎样,还是使用更为清晰的除以8的表达方式,即“y/8”。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)