什么时候印第安人变成了一个因素?

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

假设我没有错,我想知道的是,当两台计算机之间发送信息时,Endian 是否成为一个主要因素,在这两台计算机之间,Endian 可能不同,也可能不同。

  • 如果我以字符数组的形式传递一个1的短整数,而且没有修正,它是否被接收并解释为256?

  • 如果我使用下面的代码分解和重新组合短整数,那么 endianness 是否不再是一个因素?

    // 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;
    };
    
  • Is there a standard way of compensating for endianness?

Thanks in advance!

10106 次浏览

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

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

  1. 不,不过你的大概想法是对的。你忽略了一个事实,即使它通常是一个串行连接,一个网络连接(至少大多数网络连接)仍然保证正确的字节(字节)级别的字节顺序——也就是说,如果你发送一个值为0x12的字节在一个小的 endian 机器上,它仍然会被接收为0x12在一个大的 endian 机器上。

    看一个短的,如果你看十六进制的数字,它可能会有帮助。一开始是0x0001。把它分成两个字节: 0x000x01。一旦收到,就会显示为0x0100原来是256。

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

  3. 可能最简单的方法是在发送时使用 htons/htonl,在接收时使用 ntohs/ntohl。如果这还不够,还有很多替代方案,比如 XDR、 ASN.1、 CORBaIIOP、谷歌协议缓冲等等。

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

发送方和接收方都知道有线协议,如有必要,将在发送前和接收后进行转换,以便为应用程序提供正确的数据。但是这种转换发生在 在你的网络层里中,而不是在您的应用程序中。

顺便说一句,如果你在设备之间传输数据,你应该总是使用网络字节排序与 ntohlhtonlntohshtons。它将转换为 Endianness 的网络字节顺序标准,而不管您的系统和目标系统使用什么。当然,两个系统都应该这样编程——但它们通常都是在网络场景中。

非常抽象地说,endianness 是一个变量作为 char 数组的翻唱的属性。

实际上,当 read() from 和 write()到外部字节流(如文件或套接字)时,这一点非常重要。或者,再抽象地说,当你使用 连载数据时,endianness 很重要(本质上是因为序列化的数据没有类型系统,只是由哑字节组成) ; 而且,没有的 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),结果将取决于机器的最优性。


正如您可以看到的,对于整型,如果使用代数输入和输出操作,您永远不必知道自己系统的 endianness,只需知道数据流的 endianness。对于 double等其他数据类型,问题更为复杂。

你不必担心,除非你在系统的边界。通常情况下,如果你说的是 STL 的话,你已经过了那个边界了。

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

如果您只讨论内置的,那么使用 环境提供的工具提供的机器抽象就足够了

下面是一些 C/C + + 中立结构代码的准则。显然,这些都被写成了“避免的规则”... ... 所以如果代码具有这些“特性”,它可能会容易出现与 endian 相关的 bug! !(这是我在《多布斯博士》上发表的关于 Endianness 的文章)

  1. 避免使用组合不同多字节数据类型的联合。 (工会的布局可能有不同的恩典相关顺序)

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

  3. 避免使用位字段和字节掩码 (因为存储器的布局依赖于字节顺序,所以字节的屏蔽和位字段的选择是敏感的)

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

据我所知,这两种结尾都有一个优势:

  1. “大端点”在概念上更容易理解,因为它类似于我们的进位制: 最重要到最不重要。
  2. 在为多个内存大小重用内存引用时,Little-endian 非常方便。简单地说,如果您有一个指向小端 unsigned int*的指针,但是您知道存储的值小于256,那么您可以将指针强制转换为 unsigned char*