在 C 中链接移位运算符后的意外结果

Posted

技术标签:

【中文标题】在 C 中链接移位运算符后的意外结果【英文标题】:Unexpected result after chaining bit-shift operators in C 【发布时间】:2016-09-13 09:06:48 【问题描述】:

我不明白为什么链接位移操作不会返回与不链接它们相同的结果。

#include <stdio.h>

void bit_manip_func(unsigned char byte)

    unsigned char chain = (((byte >> 3) << 7) >> 3);
    printf("%d\n", chain); //this prints 144

    unsigned char o1 = byte >> 3;
    unsigned char o2 = o1 << 7;
    unsigned char o3 = o2 >> 3;
    printf("%d\n", o3); //this prints 16 as expected


int main()

    //expecting both printf's to print
    //the same value (16).
    bit_manip_func(73);
    return 0;

我希望 printf 调用都打印出 16,因为二进制中的 73 是 0100 1001。应用 byte &gt;&gt; 3 后,我应该得到 0000 1001,在 (byte &gt;&gt; 3) &lt;&lt; 7 之后,结果应该是 1000 0000,在 @987654328 之后@结果应该是0001 0000,当然是16。到底发生了什么?

【问题讨论】:

Integer promotion,每隔一段时间就会有人问这样的问题。 可能,但我不知道这与整数提升有关。 要记住一条重要的规则:int 是 C 中的一种特殊类型,所有较小的类型在计算过程中都会被提升为 int。这也是为什么为 16 位架构编译的源代码(其中int 是 16 位宽)在编译为 32 位时会有不同的行为。 Why does combining two shifts of a uint8_t produce a different result?的可能重复 Unexepected behavior from multiple bitwise shifts on the same line 【参考方案1】:

运算符 >> 和

在下面一行中,变量 byte 被提升为 int 类型,然后三个操作都在这个类型上进行:

unsigned char chain = (((byte >> 3) << 7) >> 3);

因此,最左边设置为 1 的位被保留:

01001001 => 01001 => 010010000000 => 010010000 
 ^           ^        ^               ^

在下面的代码中,变量被提升为 int 类型,但是在每次操作之后,具有 int 类型的结果被分配给一个 unsigned char 并因此换行(最高有效位被删除), 因为 unsigned char 在您的平台上的范围是 [ 0 , 2^8-1 ]。

unsigned char o1 = byte >> 3;
unsigned char o2 = o1 << 7;
unsigned char o3 = o2 >> 3;

这意味着设置为 1 的最左边的位不被保留:

01001001 => 01001 => 10000000 => 000010000
 ^           ^       

【讨论】:

这个解释太棒了 @cuvidk 谢谢,我试试。【参考方案2】:

unsigned char chain = (((byte >> 3) << 7) >> 3);

((byte &gt;&gt; 3) &lt;&lt; 7) 被提升为int,然后在int 上执行&gt;&gt; 3

包裹到unsigned char (mod 256) 你得到((73 &gt;&gt; 3) &lt;&lt; 7) &gt;&gt; 3) % 256 = 144

使用演员表:

unsigned char chain = ((unsigned char)((byte >> 3) << 7) >> 3);

【讨论】:

在一般情况下,最好将输入byte 转换为unsigned int,然后将最终结果显式转换为unsigned char。中间转换为unsigned char 仅在有意丢失最高有效位时才有意义,因为它将再次提升为int。拆分操作会更清楚。此外,右移负整数值的结果是实现定义的,因此位移操作应始终对秩为unsigned int或更高的无符号整数类型进行。固定宽度&lt;stdint.h&gt; 类型的加分。 你的意思是(unsigned char)((((unsigned int)byte &gt;&gt; 3) &lt;&lt; 7) &gt;&gt; 3))?,这不是想要的输出(16),仍然是144 不,这并不意味着对于数据丢失的特定情况。因此“在一般情况下”,仅作为附加信息。您的回答已经涵盖了这种具体情况。

以上是关于在 C 中链接移位运算符后的意外结果的主要内容,如果未能解决你的问题,请参考以下文章

c语言中的移位运算符

C语言(文件的移位与加密解密)

移位运算符的效果

移位运算符的效果

常见的关系运算符(移位运算符)

java的移位和异或运算