什么决定了一个系统是小端序还是大端序?

13

我对系统/ CPU / 程序的字节顺序感到困惑。
因此,我必须提出一些问题来澄清我的想法。

问题1

如果在我的C++程序中仅使用类型char

void main()
{
    char c = 'A';
    char* s = "XYZ";    
}

将此程序编译为名为a.out的可执行二进制文件。
a.out能在小端和大端系统上都运行吗?

问题2

如果我的Windows XP系统是小端序的,我能否在VMWare/VirtualBox中安装大端序的Linux系统?什么决定了一个系统是小端序还是大端序?

问题3

如果我想编写一个字节序无关的C++程序,我需要考虑什么?


7
你不应该期望编译好的可执行文件在除编译目标外的任何地方运行。 - Niklas B.
除非它实际上没有编译。例如托管语言。尽管即使这样也有一定的注意事项。 - N_A
@mydogisbox:当然,我是在指C++。我认为即使是托管的C++也适用(不过我不确定)。 - Niklas B.
据我所知,托管C++是在虚拟机(CLR)上运行的,因此它实际上可能更具有平台独立性。 - N_A
6个回答

21

a.out文件能否在小端和大端系统上运行?

不行,因为任何两个CPU的字节序(即大小端)不同,它们的指令集也不同。C++不同于Java,你编译的结果不是经过编译或解释的中间代码,而是直接编译成针对特定CPU的汇编代码,而字节序是CPU的一部分。

但这已经超出了字节序的问题。你可以将程序编译为针对不同CPU的可执行文件,在相应的CPU上运行正常。

是什么决定了一个系统的大小端?

就C或C++而言,决定大小端的是CPU。一个计算机中的不同处理单元可能具有不同的字节序(例如GPU可能是大端,而CPU是小端),但这种情况比较罕见。

如果我想写一个字节序无关的C++程序,需要考虑哪些因素?

只要按照C或C++的规则编写,就不必担心大小端问题。

当然,你也不能直接将文件加载到POD结构体中,或者读取一系列字节并将其视为一系列无符号短整数,然后将其处理为UTF-16编码的字符串。所有这些都涉及到实现定义的行为。

"未定义的行为"和"实现定义的行为"之间有区别。当C和C++规范说某些事情是"未定义的"时,基本上意味着可能会出现各种问题。如果你继续这么做(而且程序没有崩溃),你可能会得到不一致的结果。当它说某些事情由实现定义时,你将在该实现中获得一致的结果。

如果在VC2010中为x86编译,并假定一个字节数组是一个无符号短整数数组(例如:unsigned char *byteArray = ...; unsigned short *usArray = (unsigned short*)byteArray),那么在编译为大端CPU时,你将得到与编译为小端CPU时不同的答案。

通常,字节序问题是可以局限于输入/输出系统的事情。网络、文件读取等都应该在代码库的端点处处理好这些问题。


1
"极端值",一个很好的描述词 :) - Niklas B.

8

问题1:

是否可以在小端和大端系统上同时运行a.out?

不可以。因为a.out已经编译为特定的架构,它不能在与其不兼容的另一个架构上运行。

但是,这个简单程序的源代码没有任何可能在不同字节序机器上破坏的内容。

所以,是的,(源代码)将正常工作。(好吧……除了void main(),你应该使用int main()代替)

问题2:

如果我的WindowsXP系统是小端序,我能在VMWare/VirtualBox中安装大端序Linux系统吗?

字节序是由硬件决定的,而不是操作系统。因此,无论您在其上安装什么(本机)虚拟机,都将与主机具有相同的字节序。(因为x86都是小端序)

什么使系统成为小端或大端?

以下是在小端和大端上会有不同表现的示例:

uint64_t a = 0x0123456789abcdefull;
uint32_t b = *(uint32_t*)&a;
printf("b is %x",b)

*请注意,这违反了严格别名规则,仅供演示目的。

Little Endian : b is 89abcdef
Big Endian    : b is 1234567

在小端序中,a的低位存储在最低地址。因此,当您将a作为32位整数访问时,将读取其低32位。在大端序中,您将读取其高32位。
问题3:
如果我想编写一个字节序无关的C++程序,我需要考虑什么?
只需遵循标准的C++规则,不要做任何丑陋的事情,如上面所示的示例。避免未定义行为,避免类型转换...

从技术上讲,字节序在操作系统级别上从未被确定(除非它是虚拟操作系统)。 - N_A
那主要是我所指的。理论上,您可以模拟整个机器,包括硬件和操作系统 - 在其中任何事情都可以发生。 - Mysticial
如果你所说的理论上的东西是像VMWare、Virtual Box和Parallels这样的,那么是的。;-) http://en.wikipedia.org/wiki/Virtual_machine#Emulation_of_the_underlying_raw_hardware_.28native_execution.29 - N_A
1
感谢提到“x86全部是小端序”。 - kev

3
小端/大端是一种硬件属性。通常来说,为一台硬件编译的二进制代码无法在其它硬件上运行,除非在虚拟化环境中对机器码进行解释,并模拟目标硬件。有些双端CPU(例如ARM、IA-64)具备切换大小端的功能。
就独立于字节顺序的编程而言,真正需要处理的仅限于网络编程。存在这样的函数:ntohlhtonl,可以帮助你将硬件字节顺序转换为网络字节顺序。

感谢提到 ntohlhtonl - kev

2
首先要澄清的是大小端是硬件属性,而不是软件/操作系统属性,因此WinXP和Linux不是大端或小端,而是它们运行的硬件是大端或小端。
大小端描述了字节在数据类型中存储的顺序。大端系统先存储最高有效位(即最大值),而小端系统则先存储最低有效位。在一个系统中,每种数据类型不一定都要相同,因此可以有混合大小端系统。
小端程序无法在大端系统上运行,但这更多取决于可用的指令集,而不是编译它的系统的大小端。
如果您想编写一个字节序无关的程序,只需要不依赖于数据的字节顺序即可。

2

1: 编译器的输出取决于您给出的选项以及是否使用交叉编译器。默认情况下,它应该在您编译它的操作系统上运行,而不是其他操作系统(例如,不是所有Linux二进制文件都可以在所有Linux安装中运行)。在大型项目中,这将是您最不关心的问题,因为每个系统上的库等需要进行不同的构建和链接。使用适当的构建系统(如make)将在您无需担心的情况下处理大部分问题。

2: 虚拟机以这样一种方式抽象硬件,以允许基本上任何东西在任何其他东西中运行。只要两个操作系统在相同的硬件上运行并支持正在使用的任何虚拟化模型,它们管理内存的方式就不重要。Endianness 意味着字节顺序; 它是从左往右还是从右往左读取(或其他格式)。某些硬件同时支持两者,并且虚拟化允许两者在这种情况下共存(尽管我不知道除了理论上可能之外,这有什么用处)。但是,Linux可以在许多不同的体系结构上工作(Windows也可以在一些不同的体系结构上工作),因此情况更加复杂。

3: 如果您使用二进制运算符等操作原始内存,则可能会使自己依赖于 endianness。但是,大多数现代编程都在更高的级别上进行。因此,如果您涉及可能会施加基于 endianness 的限制的内容,您很可能会注意到。如果有这样的要求,您始终可以使用预处理器为两种 endianness 实现选项。


1
系统的字节序决定了如何解释字节,即哪一位被认为是“第一位”,哪一位被认为是“最后一位”。
只有在从程序外部的某些源(如磁盘或网络)加载或保存时才需要关注它。

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