混淆小端和大端的概念

8

我对小端和大端有些困惑。似乎我缺少了一些简单的东西。欢迎提供一些反馈。
例如,假设我们有两个函数,分别获取32位值的最低有效字节和最高有效字节:

#define LSB(x) ((x) & 0x000000FF)

#define MSB(x) ((x) & 0xFF000000)

我的问题是:上述两个函数在大端小端机器上都能返回正确的结果吗?
现在我将解释为什么我感到困惑。 假设我们在一个小端机器上。在一个小端机器上,整数9在内存中的存储方式如下(以十六进制表示):09 00 00 00(最不重要字节在前)。 现在,在某个时刻,你可能会认为,如果我们使用上述LSB函数,那么我们最终会得到这样的表达式:09 00 00 00 & 00 00 00 FF,其结果是0 - 但当然,上述LSB函数的工作方式并不是这样的。所以我觉得似乎缺少了一些东西。非常感谢任何帮助。
此外,如果我说int y = 0x000000FF - 无论机器的字节序如何,它都是255,对吗?

OT: 应该是 #define MSB(x) (((x) & 0xFF000000) >> 24) 还是只需要 #define MSB(x) ((x) >> 24)(假设传递的是32位值)? - alk
你可能需要 MSB(x) = ((x) >> 24),否则像 if (MSB(x) == 0xFF) ... 这样的代码将无法工作。 - japreiss
好的,我会研究一下,但目前我并不特别关心LSB和MSB函数的最佳实现。 - user2793162
6个回答

12
无论大小端,x & 0xFF都会给你最不重要的字节。
首先,您应该了解大小端和位数的区别。大小端意味着字节写入内存的顺序; 对于CPU中的任何计算来说,这完全无关紧要。位数表示哪些位具有较高的值; 对于任何存储系统来说,这完全无关紧要
将值从内存加载到CPU后,其大小端就无关紧要了,因为对于CPU(更准确地说是ALU),所有事情都只关心位的有效性。
因此,就C而言,0x000000FF在其最不重要的字节中具有1,使用and与变量相连,可以得到其最不重要的字节。
实际上,在整个C标准中,您找不到“endian”一词。 C定义了一个“抽象机器”,其中只有位的有效性很重要。编译器的责任是编译程序以使其与抽象机器行为相同,无论其大小端如何。因此,除非您期望某种内存布局(例如通过union或指针转换),否则根本不需要考虑大小端。
可能会对您感兴趣的另一个示例是移位。相同的事情也适用于移位。实际上,正如我之前所说,大小端对ALU毫无影响,因此<<始终向更高有效位移动,甚至不仅是编译器,而是CPU本身。
让我将这些放在两个正交方向的图表中,以便您更好地理解它。这是从CPU角度看待加载操作的方式。
在小端机器上:
         MEMORY            CPU Register

  LSB BYTE2 BYTE3 MSB  ---->   MSB
    \    \     \----------->  BYTE3
     \    \---------------->  BYTE2
      \-------------------->   LSB

在大端序计算机上:

         MEMORY            CPU Register

      /-------------------->   MSB
     /    /---------------->  BYTE3
    /    /     /----------->  BYTE2
  MSB BYTE3 BYTE2 LSB  ---->   LSB

如您所见,在这两种情况下,您都有:

CPU Register

    MSB
   BYTE3
   BYTE2
    LSB

这意味着在两种情况下,CPU最终加载了完全相同的值。


请查看我解释为什么会有混淆的那一段,或许对你来说更清楚哪一部分的问题是我最困惑的(例如,我谈到 09 00 00 00 和 00 00 00 FF 的地方)。 - user2793162
@dmcr_code,我已经解决了你的困惑。事实上,0x0000009内存中以09 00 00 00的形式存储,但是ALU仍然将其视为0x0000009。对于ALU来说,不存在字节序,只有重要性。当你将寄存器加载/存储到内存时,字节序会被处理,但这仅仅是存储的问题,而不是处理的问题。 - Shahbaz
@dmcr_code,完全正确。在处理过程中,存储并不重要。因此,如果您的数字是 0x00000009,即使内存将其存储为 巴法门图腾,它仍然是 0x00000009,将其与 0x000000FF 进行 and 操作将会得到 9 - Shahbaz
是的,基本上即使在内存中存储了 09 00 00 00,当这个值被加载时,它将被正确解释并获得值 9(假设我们在小端机器上)。而 0x000000FF 总是 255 - 因为这就是我们写的方式,大端方式。 - user2793162
@dmcr_code,没错。我们在纸上从左到右“存储”数字。如果某个奇怪的国家从右到左书写数字,那么 90000000 仍然是 9,对吧? ;) 在内存中存储也是一样的。 - Shahbaz
显示剩余3条评论

3

0x000000FF始终表示255,与字节序无关。它在小端机器上存储为FF 00 00 00,因此LSB(9)仍然有效。


1

是的,无论大小端如何,这些都可以正常工作。

你用作掩码的数字和输入的数字具有相同的字节序,因此无论如何都会得到相同的结果。

字节序主要成为一个问题,当你有一个整数,例如通过网络连接作为char数组接收到时。在这种情况下,你必须按正确的顺序将这些char重新组合以获取原始值。


1
我的问题是:上述两个函数在大端和小端机器上都返回正确的结果吗?
是的,它们会。问题出现在你想从一个多字节数组中形成一个标量时,这不是你正在做的事情。

0
只要您将整数值视为单个实体而不是原始字节序列(在内存中、在线路上传输等),则字节序问题就不会出现在您的代码中。
因此,0x000000FF始终是255,您的LSB和MSB宏是正确的。

我似乎对其他事情感到困惑。那部分我在谈论09 00 00 00&00 00 00 FF等的段落中已经解释过了。 - user2793162

0

Endian 关乎内存使用。主要需要关注的是在将字节序列化或反序列化到内存、存储器或某种流时。

我相信,根据你的使用方式,宏有时可能如预期般工作,有时可能不工作。如果 x 是 int(假设你正在使用 32 位整数),那么你应该没问题,因为编译器知道 int 是什么以及当 x 不是 32 位数字时它是如何表示的,否则可能会遇到问题。


关于参数大小的观点很好,尽管问题是另外一回事。 - Shahbaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接