3

好的,我觉得问这个很愚蠢,但是为什么下面的代码输出不同的行?

要打印第一行,我将地址指向数组的第一个字节,将其解释为指向 uint16_t 的指针,获取值并逐个打印它。

对于第二行,我获取指向第一个字节的指针,将其解释为指向 uint8_t 的指针,获取值并逐个打印它。然后对第二个字节做同样的事情。

由于我不修改为数组分配的内存,只以不同的方式解释它,我希望输出相同,但字节顺序不同。

我可能错过了一些东西,但我唯一的猜测是间接运算符做了我没想到的事情。

#include <iostream>
#include <string.h>


 int main() {
   uint8_t u[2];
   u[0] = 170;
   u[1] = 85;

  for(int i = 15; i >= 0; --i) {
    printf( "%u", (((*((uint16_t*)u)) >> i) & 0x0001));
  }
  printf( "\n");
  for(int i = 7; i >= 0; --i) {
    printf( "%u", (((*((uint8_t*)u)) >> i) & 0x01));
  }
  for(int i = 7; i >= 0; --i) {
    printf( "%u", (((*((uint8_t*)(u + 1))) >> i) & 0x01));
  }
}

输出

0101010110101010 
1010101001010101

更新#1:请忽略分配,是的,示例代码不适用于每个操作系统,但这只是一个简化的示例。

更新#2:我知道字节序,但我错过的是逻辑与物理位表示。在上面的示例中,即使物理表示未更改,我也会打印受字节序影响的逻辑表示。非常感谢@john-kugelman 对此的解释。

4

3 回答 3

6

在基于 Intel 的平台上,数字以little endian 顺序存储。最低有效字节在前,最高有效字节在后。这与我们通常阅读数字的方式相反。如果我们用 little endian 而不是 big endian 顺序写数字,则将写成 10233201而不是1023

当您将字节数组中的字节解释为 16 位整数时,第一个字节 (170) 被解释为最低有效字节,第二个字节 (85) 被解释为最高有效字节。但是当您自己打印字节时,您会以相反的顺序打印它们。这就是不匹配的来源。

字节顺序是特定于平台的属性。大多数非英特尔架构使用更“自然”的大端顺序。不幸的是,基于英特尔的架构是最常见的。碰巧的是,几乎所有的网络流量都是大端的,也称为“网络字节序”。当基于 Intel 的机器在 Internet 上交谈时,它们在发送和接收数据期间会进行大量字节交换。

如果我打印 uint16_t 本身,我预计会发生这种不匹配。我不明白为什么当我尝试获取它的位时会发生这种情况。

使用位掩码和移位操作读取其位不会从左到右读取内存中的物理位,而是从最高有效位到最低有效位读取逻辑位。在小端架构上,从最高到最低有效等同于从右到左的顺序。

另请注意,字节顺序意味着交换字节,而不是。在小端架构中不交换位,字节是。位不能交换,因为它们不能单独寻址。你只能通过轮班和面具来对付他们。

于 2015-08-28T23:41:33.620 回答
1

撇开可能的对齐错误和缺少格式字符串长度修饰符,您会遇到字节序问题。该术语描述了长于最小可寻址单元(即字节)的数据类型如何存储在内存中。

您的系统似乎对 16 位整数使用 little endian:低字节存储在低地址。

请注意,没有理由为后两个 for 循环进行强制转换,因为您使用的类型与数组元素相同。永远不要在没有充分理由的情况下进行转换,并始终尝试编写您不必转换的代码。强制转换会阻止编译器帮助您检测类型不匹配。因此,只有在绝对确定自己比编译器更了解自己在做什么的情况下,才可以进行强制转换。

于 2015-08-28T23:41:39.833 回答
1

内存中的两个字节:

0xAA

0x55

当它们被解释为 16 位字时,有两个可能的值。

基于处理器的字节顺序:

Little Endian(最低有效字节在前):0x55AA // Intel x86/x64

Big Endian(最高有效字节在前):0xAA55 // 电源、ARM 等。

于 2015-08-28T23:46:54.210 回答