Endianness什么时候成为一个因素?

时间:2011-08-24 17:45:12

标签: c++ networking stl endianness

根据我的理解,字节顺序是组成多字节字的字节顺序不同,至少在最典型的情况下。这样一个16位整数可以存储为0xHHLL0xLLHH

假设我没有那么错,我想知道的是,当Endian可能会或可能没有不同的两台计算机之间发送信息时,Endianness何时成为主要因素。

  • 如果我以char数组的形式传输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;
    };
    
  • 是否有补偿字节序的标准方法?

提前致谢!

8 个答案:

答案 0 :(得分:50)

非常抽象地说,字节序是将变量重新解释为char数组的属性。

实际上,当你从read()write()到外部字节流(如文件或套接字)时,这很正确。或者,再次抽象地说,当你序列化数据时,字节序很重要(主要是因为序列化数据没有类型系统,只是由哑字节组成);和endianness 从一个到另一个是您需要深入细节的地方。

即做 - 写作:

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,问题就更复杂了。

答案 1 :(得分:35)

对于记录,如果您在设备之间传输数据,则几乎总是使用ntohlhtonlntohshtons的网络字节顺序。无论您的系统和目标系统使用什么,它都将转换为Endianness的网络字节顺序标准。当然,这两个系统都应该像这样编程 - 但它们通常都在网络场景中。

答案 2 :(得分:7)

  1. 不,虽然你确实有正确的总体想法。你所缺少的是这样的事实,即使它通常是串行连接,网络连接(至少大多数网络连接)仍然保证八位字节(字节)级别的正确字节顺序 - 即,如果你发送一个带有值的字节在小端机器上的0x12,在大端机器上它仍然会被接收为0x12。

    看一下,如果你看一下十六进制的数字,它可能会有所帮助。它从0x0001开始。你将它分成两个字节:0x00 0x01。收到后,将被读为0x0100,结果为256.

  2. 由于网络处理八位字节级别的字节顺序,通常只需要补偿字节顺序,而不是字节内的位。

  3. 可能最简单的方法是在发送时使用htons / htonl,在接收时使用ntohs / ntohl。当/如果这还不够时,有许多替代方案,如XDR,ASN.1,CORBA IIOP,Google协议缓冲区等。

答案 3 :(得分:6)

补偿的“标准方式”是“网络字节顺序”的概念已被定义,几乎总是(AFAIK)作为大端。

发送者和接收者都知道有线协议,如果需要,将在发送之前和接收之后进行转换,以便为应用程序提供正确的数据。但是,此转换发生在网络层内部,而不是在您的应用程序中。

答案 4 :(得分:6)

这两个结尾都有我所知道的优势:

  1. Big-endian在概念上更容易理解,因为它类似于我们的位置数字系统:最重要到最不重要。
  2. 对于多个内存大小重用内存引用时,Little-endian很方便。简单地说,如果你有一个指向小端unsigned int*的指针,但你知道存储的值是&lt; 256,您可以将指针投射到unsigned char*

答案 5 :(得分:5)

Endianness总是一个问题。有人会说,如果你知道连接到网络的每个主机运行相同的操作系统等,那么你就不会有问题。这是真的,直到它不是。您始终需要发布详细说明在线数据的EXACT格式的规范。它可以是您想要的任何格式,但每个端点都需要理解格式并能够正确解释它。

一般来说,协议使用big-endian来表示数值,但如果每个人都不兼容IEEE 754等,这就有局限性。如果你可以节省开销,那么使用XDR(或你最喜欢的解决方案)并保证安全

答案 6 :(得分:4)

以下是一些C / C ++ endian-neutral代码的指南。显然这些被写成“要避免的规则”......所以如果代码具有这些“功能”,它可能容易出现与字节序相关的错误! (这是我在Dobbs博士上发表的关于Endianness的文章)

  1. 避免使用组合了不同多字节数据类型的联合。 (工会的布局可能有不同的与端相关的订单)

  2. 避免访问byte数据类型之外的字节数组。 (字节数组的顺序具有与字节序相关的顺序)

  3. 避免使用位字段和字节掩码 (由于存储的布局取决于字节顺序,字节的掩码和位字段的选择是字节序敏感的)

  4. 避免将指针从多字节类型转换为其他字节类型 (当指针从一种类型转换为另一种类型时,源的字节顺序(即原始目标)将丢失,后续处理可能不正确)

答案 7 :(得分:3)

除非你在系统的边界,否则你不必担心。通常,如果您正在谈论stl,那么您已经通过了该边界。

序列化协议的任务是指示/确定如何将一系列字节转换为您正在发送的类型,内置类型或自定义类型。

如果你只是内置说话,你可以满足tools provided by your environment提供的机器抽象