什么时候Endianness成为一个因素
When does Endianness become a factor?
根据我的理解,是指组成多字节字的字节顺序不同,至少在最典型的情况下是这样。因此,一个16位整数可以存储为0xHHLL
或0xLLHH
。
假设我没有错,我想知道的是,当两台计算机之间发送信息时,端序何时成为一个主要因素,其中端序可能是不同的,也可能不是。
-
如果我发送一个短整数1,在一个字符数组的形式,没有校正,它是接收和解释为256?
-
如果我用下面的代码分解和重组短整数,是否字节序不再是一个因素?
// Sender: for(n=0, n < sizeof(uint16)*8; ++n) { stl_bitset[n] = (value >> n) & 1; }; // Receiver: for(n=0, n < sizeof(uint16)*8; ++n) { value |= uint16(stl_bitset[n] & 1) << n; };
-
是否有一种标准的方式来补偿端序?
非常抽象地说,端序性是将变量重新解释为字符数组的属性。
实际上,当您从read()
和write()
到外部字节流(如文件或套接字)时,这一点非常重要。或者,抽象地说,当序列化数据时,端序很重要(本质上是因为序列化的数据没有类型系统,只是由哑字节组成);在编程语言中,与无关,因为该语言只对值进行操作,而不对表示进行操作。从一个到另一个是你需要深入研究细节的地方。
with - writing:
uint32_t n = get_number();
unsigned char bytesLE[4] = { n, n >> 8, n >> 16, n >> 24 }; // little-endian order
unsigned char bytesBE[4] = { n >> 24, n >> 16, n >> 8, n }; // big-endian order
write(bytes..., 4);
这里我们可以只写reinterpret_cast<unsigned char *>(&n)
,结果将取决于系统的端序。
和阅读:
unsigned char buf[4] = read_data();
uint32_t n_LE = buf[0] + buf[1] << 8 + buf[2] << 16 + buf[3] << 24; // little-endian
uint32_t n_BE = buf[3] + buf[2] << 8 + buf[1] << 16 + buf[0] << 24; // big-endian
同样,这里我们可以说,uint32_t n = *reinterpret_cast<uint32_t*>(buf)
,结果将取决于机器的端序。
可以看到,对于整型,如果使用代数输入和输出操作,您不必知道自己系统的端序,只需知道数据流的端序。对于其他数据类型(如double
),问题更为复杂。
作为记录,如果您在设备之间传输数据,您应该几乎总是使用网络字节排序与ntohl
, htonl
, ntohs
, htons
。无论您的系统和目标系统使用什么,它都将转换为网络字节顺序标准。当然,这两个系统都应该这样编程——但它们通常是在网络场景中。
-
不,虽然你的大致想法是对的。你忽略了一个事实,即使它通常是一个串行连接,网络连接(至少大多数网络连接)仍然保证在八位字节(字节)级别上正确的端序——也就是说,如果你在小端序机器上发送一个值为0x12的字节,它在大端序机器上仍然会被接收为0x12。
看一个简短的,如果你看十六进制的数字,可能会有帮助。它从0x0001开始。将其分解为两个字节:0x00 0x01。在接收时,它将被读取为0x0100,结果是256。
-
由于网络在八位字节级别处理端序,所以通常只需要补偿字节的顺序,而不是字节中的位。
-
可能最简单的方法是在发送时使用htons/htonl,在接收时使用ntohs/ntohl。当/如果这还不够,还有许多替代方案,如XDR, ASN.1, CORBA IIOP, Google协议缓冲区等。
补偿的"标准方式"是"网络字节顺序"的概念已经定义,几乎总是(AFAIK)为大端序。
发送方和接收方都知道有线协议,如果有必要,会在发送前和接收后进行转换,以给应用程序正确的数据。但是这种转换发生在网络层中,而不是在应用程序中。
两个字符都有我所知道的优势:
- 大端序在概念上更容易理解,因为它类似于我们的位置数字系统:从最高有效到最低有效。当为多个内存大小重用内存引用时,
- Little-endian很方便。简单地说,如果你有一个指向小端
unsigned int*
的指针,但你知道那里存储的值是<你可以将指针强制转换为unsigned char*
。>
序位总是一个问题。有人会说,如果你知道连接到网络的每台主机都运行相同的操作系统,那么你就不会有问题。这是真的,直到它不是。您总是需要发布一个详细说明在线数据的EXACT格式的规范。它可以是你想要的任何格式,但每个端点都需要理解该格式并能够正确地解释它。
通常,协议使用大端位数表示数值,但如果每个人都不兼容IEEE 754等,这有局限性。如果您可以承担开销,那么使用XDR(或您最喜欢的解决方案)并确保安全。
这里有一些关于C/c++端中立代码的指导原则。显然,这些都是"避免规则"……所以,如果代码具有这些"特性",那么它可能容易出现与尾端相关的bug !!(这是我在Dr Dobbs上发表的关于Endianness的文章)
-
避免使用联合来组合不同的多字节数据类型。
-
避免访问字节数据类型以外的字节数组。
-
避免使用位域和字节掩码(由于存储的布局依赖于端序,字节的屏蔽和位字段的选择是端序敏感的)
-
避免将指针从多字节类型转换为其他字节类型。
(当指针从一种类型强制转换为另一种类型时,源的端序(即:原始目标)丢失,后续处理可能不正确)
您不必担心,除非您处于系统的边界。通常情况下,如果你在谈论stl,你已经越过了那个边界。
序列化协议的任务是指示/确定如何将一系列字节转换为您要发送的类型,是内置类型还是自定义类型。
如果您只讨论内置的,那么您可以使用由您的环境提供的工具提供的机器抽象]
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 运行同一解决方案的另一个项目的项目
- 挂起和取消挂起一个文件DLL
- 用C++中的一个变量定义一个常量
- 函数向量_指针有不同的原型,我可以构建一个吗
- 在c++中用vector填充一个简单的动态数组
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- 预处理器:插入结构名称中的前一个行号
- 我在c++代码中生成了一个运行时#3异常
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 从链接列表c++中删除一个项目
- 告诉一个 const char 数组,除了编译时 C 样式的字符串外,它不以 '