间接运算符是否会更改内存表示?

Does Indirection operator change memory representation?

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

为了打印第一行,我获取数组第一个字节的地址,将其解释为指向 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:请忽略分配,是的,示例代码并不适用于每个 os,但它只是一个简化的示例。

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

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

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

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

I expected this missmatch to happen if I print that uint16_t itself. What I don't understand is why it happens when I try to get its bits.

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

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

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

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

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

内存中的两个字节:

0xAA

0x55

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

基于处理器的字节顺序:

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

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