【问题标题】:Byte-wise output of uint32_tuint32_t 的逐字节输出
【发布时间】:2013-01-27 15:22:52
【问题描述】:

我正在尝试更好地理解位运算符。我有许多类型 uint32_t 我正在尝试逐字节输出。执行此操作的代码是:

void printByteWise(uint32_t num) {

  printf("byte 1 = %u\n", (num & 0xFF000000));
  printf("byte 2 = %u\n", (num & 0x00FF0000));
  printf("byte 3 = %u\n", (num & 0x0000FF00));
  printf("byte 4 = %u\n", (num & 0x000000FF));
}

假设上面代码示例中的num 是9。那么字节数组应该像这样存储在内存中:
09 00 00 00(按地址升序排列)。如果是这样,输出应该是:
byte 1 = 09
byte 2 = 00
byte 3 = 00
byte 4 = 00
但我得到的输出是:

byte 1 = 0
byte 2 = 0
byte 3 = 0
byte 4 = 9

我在一个小端的系统上,它是这样获得的:

int is_bigEndian() {
  int i = 1;
  char *low = (char *) (&i);
  return *low ? 0 : 1;
}  

这种行为正确吗?为什么我会看到这种行为?

【问题讨论】:

  • num & 0xFF000000 的结果不是字节。它仍然是一个 32 位整数,低 24 位设置为 0。
  • %u 告诉 printf 期待一个无符号整数,而不是 uint32_t。因此,您的代码会调用未定义的行为。考虑将 PRIu32 宏用作格式字符串,用于 uint32_t 值。
  • 另外,你不能使用 is_bigEndian 来测试小端,因为混合端系统的可能性。
  • @modifiablelvalue: 我如何检查混合字节序?
  • @Sriram 这不重要。一个更好的主意是编写不关心整数的底层表示的代码。例如,在通过 Internet 接收和发送整数值时,使用 sscanf 和 sprintf 将整数值解码和编码为十进制数字字符串。

标签: c bit-shift endianness


【解决方案1】:

请记住,您的两个操作数都是相同的字节序。

在 little endian 上,是的,9 将存储为 0x09000000。然后,您将使用 0xFF000000 进行掩码,它将作为 0x000000FF 存储在内存中,因此将在该模式中用作掩码。

如果您想全面了解效果,请按照 Ali Veli 的说明进行操作,并使用 char 指针逐字节遍历内存。

【讨论】:

  • 但是0xFF000000 不是常量吗?那是否也“存储”在内存中?这看起来像个菜鸟问题,但我正在努力学习。
  • @Sriram - 是的,它是一个常数,但它也位于您的程序中。执行 OR 操作类似于OR %eax,#FF000000,其中常量在您的程序中并且仍然发布给您的处理器,然后使用相同的字节序进行“解释”。请记住,程序不是魔术,指令仍然需要编码和存储;)。
  • 这里的 eax 与 FF000000 究竟是如何进行或运算的?操作数存储在哪里以供 OR 在执行期间对其进行处理?
  • 假设 eax 现在已经包含 9。操作数存储在内存中,然后缓存,然后在指令中发送到处理器。在这种情况下,您的立即数 (0xFF000000) 在指令中编码,因此一旦发出,整个 OR 指令(包括立即数)就会发出并存储在指令缓冲区中。然后由设置 ALU 的处理器对其进行解码。最后,然后执行。即使您将 0xFF000000 加载到寄存器中并执行OR %eax %ebx,您也会获得相同的效果。
  • 但这里涵盖位移运算符的答案使其更加混乱:stackoverflow.com/a/7184905/350685
【解决方案2】:

使用位运算符,您的操作数实际上是寄存器中的数字而不是内存中的数字。所以这里与字节序无关,这是预期的和正确的。

如果您通过将变量的地址转换为 char * 或其他内容来检查变量并通过增加指针的值来遍历字节,那么您每次都会从内存中读取一个字节(假设缓存是透明的)并且您会看看字节序的效果。

【讨论】:

  • 如果 downwoter 能说出原因,我将不胜感激
  • 取消了,因为您的大部分答案都是正确的,但这与内存与寄存器无关。
  • 好吧,例如,按位运算符的 afaik 操作数在寄存器上,那么我无法解释为什么向左移动 1 个位置总是将数字乘以 1,而不管字节顺序如何。如果你有更多的见解,请赐教:)
  • @AliVeli 但是寄存器仍然可以使用与处理器相同的字节序。左移将始终乘以 2,因为 ALU 也使用相同的字节序,并且以可以正确理解的方式连接起来。操作不会神奇地发生,它们仍然需要通过 ALU,从而通过电线到达正确的位置。
  • 我倾向于不同意,因为stackoverflow.com/a/4505750/767543,例如cs.umd.edu/class/sum2003/cmsc311/Notes/Data/endian.html(寄存器既不是大端也不是小端。它只是一个保存32位值的寄存器)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-14
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-06
  • 1970-01-01
相关资源
最近更新 更多