移位是否取决于字节序?


156

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

在Little-Endian机器上:

00000001 00000100 00000000 00000000

在Big-Endian机器上:

00000000 00000000 00000100 00000001

现在,如果我在10位上应用左移(即:numb << = 10),则应该具有:

[A]在Little-Endian机器上:

正如我在GDB中注意到的那样,Little Endian进行了3个步骤的左移:[我展示了'3'个步骤,只是为了更好地理解处理过程]

  1. 请不要 在Big-Endian Convention中:

    00000000        00000000        00000100    00000001
  2. 应用左移:

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

    00000000        00000100        00010000        00000000 

[B]。在Big-Endian机器上:

00000000        00010000        00000100        00000000

我的问题是:

如果我直接在Little Endian Convention上应用Left Shift,它应该给出:

numb

00000001 00000100 00000000 00000000

numb << 10

00010000 00000000 00000000 00000000

但实际上,它给出了:

00000000        00000100        00010000        00000000 

仅为了获得第二个结果,我已经在上面显示了三个假设步骤。

请向我解释以上两个结果为何不同的原因:的实际结果numb << 10与预期结果不同。

Answers:


194

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

更新,感谢@jww:在PowerPC上,向量的移位和旋转对字节序敏感。您可以在向量寄存器中有一个值,移位将在little-endian和big-endian上产生不同的结果


4
感谢您的解释。您能否提出一些参考,以便我更好地理解这种复杂性。
Sandeep Singh的

4
理解字节序的最好方法是在嵌入式级别上将其真正用于不同的体系结构。但是,我可以向您推荐这两篇文章:codeproject.com/KB/cpp/endianness.aspxibm.com/developerworks/aix/library/au-endianc/…–
Carl

3
所以我的代码不管字节序如何都可以工作?这很棒!我一直很担心我不得不将我的代码乱成一团糟!
MarcusJ

2
@MarcusJ:不一定。例如,如果您正在从表示32位整数的文件中读取4个字节,则需要将读取的数据的字节序与接收数据的系统的字节序一起考虑,以便正确解释数据。
卡尔

3
在PowerPC上,向量的移位和旋转对字节序敏感。您可以在向量寄存器中有一个值,而移位将在小端和大端上产生不同的结果。
jww

58

不,像C的其他任何部分一样,位移是根据而不是表示形式定义的。左移1是复数乘2,右移是除法。(一如往常,在使用按位运算时,请注意有符号性。对于无符号整数类型,所有内容都定义最充分。)


1
对于整数算术,这基本上是正确的,但是C确实提供了很多与表示相关的行为。
埃德蒙(Edmund)

2
@Edmund:嗯...最值得注意的是,没有指定有符号性的实现,因此,对负整数定义了按位运算(如右移)以及取模和除法的行为。您还有什么其他事情是实现定义的?
Kerrek SB 2011年

不幸的是,@ KerrekSB不是在负整数上定义的实现。它们在C89中未指定,在C99 +中未定义,这是一个非常糟糕的主意。
Paolo Bonzini '16

@PaoloBonzini:是的,很好。实际上,这甚至更好,因为它加强了以下观点:移位操作是根据值定义的,当结果无法表示时可能未定义,并且推测基本表示没有帮助。
Kerrek SB 2013年

@KerrekSB:问题是每个人实际上都需要左移,以便根据情况将其既表示为值又表示为。使用无符号整数可能会导致其他问题,例如,x &= -1u << 20如果x是64位和int32 位,则很可能是不正确的。因此,GCC承诺永远不会将带符号的班次视为未定义甚至未指定。
Paolo Bonzini 2016年

5

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


4

计算机不会像我们那样写下数字。值只是移动。如果您坚持一个字节一个字节地查看它(即使那不是计算机的工作方式),则可以说在小字节序计算机上,第一个字节向左移动,多余的位进入第二个字节,等等。

(顺便说一句,如果您垂直而不是水平地写字节,而在高位地址上写,则低端字节排序更有意义。这恰好是通常绘制内存映射图的方式。)


2

尽管公认的答案指出,持久性是从内存角度来看的一个概念。但是我不认为直接回答这个问题。

一些答案告诉我,按位运算不依赖于字节序,处理器可以用任何其他方式表示字节。无论如何,这是在谈论那些偏执狂。

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

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

0x1F & 0xEF

您将如何在纸上手工计算结果?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

因此,这里我们使用Big Endian格式进行计算。您还可以使用Little Endian计算并获得相同的结果。

顺便说一句,当我们用代码写数字时,我认为它就像是Big Endian格式。123456要么0x1F,最重要的数字从左开始。

同样,一旦我们在纸上编写了某种二进制格式的值,我认为我们已经选择了Endianess,并且正在从内存中查看该值。

回到问题所在,<<应该将移位操作视为从LSB(最低有效字节)转换为MSB(最高有效字节)

然后以问题中的示例为例:

numb=1025

小端

LSB 00000001 00000100 00000000 00000000 MSB

所以<< 1010bit转向从LSB到MSB。


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

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 << 1
LSB 00000000 00000001 MSB不是 LSB 01000000 00000000 MSB

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

所以就像

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


总结一下:

  1. 虽然据说按位运算是抽象的……但是,当我们手动计算按位运算时,在纸上写下二进制格式时,我们仍然需要知道我们正在使用什么字节序。另外,我们需要确保所有操作员都使用相同的字节序。

  2. OP之所以未能获得预期的结果,是因为他做错了比赛。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.