【问题标题】:Does bit-shift depend on endianness?位移是否取决于字节顺序?
【发布时间】:2011-11-03 07:53:52
【问题描述】:

假设我有数字 'numb'=1025 [00000000 00000000 00000100 00000001] 代表:

在 Little-Endian 机器上:

00000001 00000100 00000000 00000000

在大端机器上:

00000000 00000000 00000100 00000001

现在,如果我在 10 位上应用左移(即:numb

[A] 在 Little-Endian 机器上:

正如我在 GDB 中注意到的,Little Endian 分 3 个步骤执行左移:[我已经展示了 '3' 个步骤以更好地理解处理过程]

  1. 治疗没有。在大端约定中:

    00000000        00000000        00000100    00000001
    
  2. 应用左移:

    00000000        00010000        00000100        00000000
    
  3. 在 Little-Endian 中再次表示结果:

    00000000        00000100        00010000        00000000 
    

[B]。在大端机器上:

00000000        00010000        00000100        00000000

我的问题是:

如果我直接在 Little Endian 上应用左移 约定,它应该给出:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

但实际上,它给出了:

00000000        00000100        00010000        00000000 

为了实现第二个结果,我在上面展示了三个假设步骤。

请解释一下为什么上述两个结果不同:numb << 10的实际结果与预期结果不同。

【问题讨论】:

    标签: c endianness


    【解决方案1】:

    字节序是值存储在内存中的方式。当加载到处理器中时,无论字节顺序如何,移位指令都会对处理器寄存器中的值进行操作。因此,从内存加载到处理器相当于转换为大端,然后进行移位操作,然后将新值存储回内存中,这就是小端字节顺序再次生效的地方。

    更新,感谢@jww:在 PowerPC 上,矢量移位和旋转是字节序敏感的。您可以在向量寄存器中拥有一个值和一个移位will produce different results on little-endian and big-endian

    【讨论】:

    • 感谢您的解释。您能否建议一些参考资料,以便我可以更好地理解这些复杂性。
    • 理解字节序的最佳方法是在嵌入式级别的不同架构上真正使用它。不过,我可以参考这两篇文章:codeproject.com/KB/cpp/endianness.aspxibm.com/developerworks/aix/library/au-endianc/…
    • 所以我的代码不管字节序都可以工作?!这很棒!我一直很担心我不得不将我的代码破解并返回!
    • @MarcusJ:不一定。例如,如果您从代表 32 位整数的文件中读取 4 个字节,则需要考虑正在读取的数据的字节序以及接收数据的系统的字节序,以便正确解释数据。
    • 在 PowerPC 上,矢量移位和旋转是字节序敏感的。您可以在向量寄存器中有一个值,并且移位会在 little-endian 和 big-endian 上产生不同的结果。
    【解决方案2】:

    不,bitshift 与 C 的任何其他部分一样,是根据 定义的,而不是表示形式。左移 1 是乘以 2,右移是除法。 (在使用按位运算时,请注意符号性。对于无符号整数类型,一切都是最明确的。)

    【讨论】:

    • 这对于整数算术来说基本上是正确的,但 C 确实提供了大量依赖于表示的行为。
    • @Edmund: 嗯...最值得注意的是没有指定符号的实现,因此按位运算(如右移)和模和除的行为是在负数上定义的整数。您还想到了哪些实现定义的其他事项?
    • @KerrekSB 不幸的是,它们不是在负整数上定义的实现。它们在 C89 中未指定,在 C99+ 中未定义,这是一个非常糟糕的主意。
    • @PaoloBonzini:是的,好点子。实际上这甚至更好,因为它强调了移位操作是根据值定义的这一点,当结果不可表示时可能是未定义的,并且推测底层表示也无济于事。
    • @KerrekSB:问题是每个人实际上都需要左移来表示为值和表示,这取决于具体情况。并且使用无符号整数可能会导致其他问题,例如,如果 x 是 64 位而 int 是 32 位,x &= -1u << 20 很可能是不正确的。出于这个原因,GCC 承诺永远不会将带符号的班次视为未定义甚至未指定。
    【解决方案3】:

    首先移出高位的移位指令被认为是左移。无论哪个移位指令首先移出低位,都被认为是右移。从这个意义上说,>><<unsigned 数字的行为将不依赖于字节序。

    【讨论】:

      【解决方案4】:

      计算机不会像我们那样写下数字。价值只是转移。如果您坚持逐字节查看它(即使计算机不是这样做的),您可以说在小端机器上,第一个字节向左移动,多余的位进入第二个字节,等等。

      (顺便说一句,如果您垂直而不是水平写入字节,并且在顶部有更高的地址,那么 little-endian 更有意义。这恰好是通常绘制内存映射图的方式。)

      【讨论】:

        【解决方案5】:

        尽管接受的答案指出字节序是内存视图中的一个概念。但我认为这不能直接回答问题。

        一些答案​​告诉我,位运算不依赖于字节序,处理器可能以任何其他方式表示字节。无论如何,它是在谈论字节序被抽象化。

        但是,例如,当我们在纸上进行一些按位计算时,不需要首先说明字节序吗?大多数时候,我们会隐式选择字节序。

        例如,假设我们有这样一行代码

        0x1F & 0xEF

        你会如何在纸上手工计算结果?

          MSB   0001 1111  LSB
                1110 1111
        result: 0000 1111
        

        所以这里我们使用 Big Endian 格式来进行计算。也可以使用 Little Endian 计算,得到同样的结果。

        顺便说一句,当我们在代码中编写数字时,我认为它就像是大端格式。 1234560x1F,最重要的数字从左边开始。

        再次,一旦我们在纸上写了一些二进制格式的值,我认为我们已经选择了 Endianess,并且我们正在查看我们从内存中看到的值。

        回到这个问题,移位操作<< 应该被认为是从 LSB(最低有效字节)转移到 MSB(最高有效字节)

        那么问题中的例子:

        numb=1025

        小端序

        LSB 00000001 00000100 00000000 00000000 MSB

        所以 << 10 将是 10bit 从 LSB 转移到 MSB。


        Little Endian 格式的比较和<< 10 操作一步一步:

        MSB                                        LSB
            00000000  00000000  00000100  00000001  numb(1025)
            00000000  00010000  00000100  00000000  << 10
        
        LSB                                        MSB
            00000000  00000100  00010000  00000000 numb(1025) << 10, and put in a Little Endian Format
        
        LSB                                        MSB
            00000001  00000100  00000000  00000000 numb(1205) in Little Endian format
            00000010  00001000  00000000  00000000 << 1 
            00000100  00010000  00000000  00000000 << 2 
            00001000  00100000  00000000  00000000 << 3 
            00010000  01000000  00000000  00000000 << 4
            00100000  10000000  00000000  00000000 << 5
            01000000  00000000  00000001  00000000 << 6
            10000000  00000000  00000010  00000000 << 7
            00000000  00000001  00000100  00000000 << 8
            00000000  00000010  00001000  00000000 << 9
            00000000  00000100  00010000  00000000 << 10 (check this final result!)
        

        哇!我得到了 OP 描述的预期结果!

        OP没有得到预期结果的问题是:

        1. 他好像没有从 LSB 转到 MSB。

        2. 当以 Little Endian 格式移位时,您应该意识到(感谢上帝,我意识到了):

        LSB 10000000 00000000 MSB &lt;&lt; 1
        LSB 00000000 00000001 MSB不是 LSB 01000000 00000000 MSB

        因为对于每个个体8bits,我们实际上是以MSB 00000000 LSB Big Endian 格式编写的。

        原来如此

        LSB[ (MSB 10000000 LSB) (MSB 00000000 LSB) ]MSB


        总结一下:

        1. 虽然据说按位运算是抽象出来的 blablablabla...,但当我们手动计算按位运算时,我们仍然需要知道我们使用的是什么字节序,因为我们在纸上写下二进制格式。此外,我们需要确保所有运算符使用相同的字节序。

        2. OP 没有得到预期的结果是因为他换错了。

        【讨论】:

          猜你喜欢
          • 2020-11-27
          • 2014-10-13
          • 2020-01-13
          • 1970-01-01
          • 1970-01-01
          • 2022-01-23
          • 2013-11-05
          • 1970-01-01
          • 2021-10-06
          相关资源
          最近更新 更多