C语言中的字节序宏

3

我最近看到了这篇关于C语言中字节序宏的帖子,但是第一个答案让我有些难以理解。

支持任意字节序的代码,可以被放进一个名为order32.h的文件中:

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

您可以通过以下方式检查小端系统: 我了解字节序的一般概念。这是我对代码的理解:
1. 创建小端、中间端和大端的示例。 2. 将测试用例与小端、中端和大端的示例进行比较,并决定主机机器的类型。
我不明白以下几个方面:
1. 为什么需要联合存储测试用例?不能保证能够像需要的那样容纳32位/4字节吗?赋值{ { 0, 1, 2, 3 } }的含义是什么?它将值分配给联合,但为什么要用两个大括号的“奇怪”的标记? 2. 为什么要检查CHAR_BIT?一个评论提到检查UINT8_MAX会更有用?char在这里为什么要使用,因为它不能保证是8位宽的?为什么不直接使用uint8_t?我找到了Google-Devs github的链接。他们没有依赖于此检查...请问有人能详细说明吗?
3个回答

3

为什么需要使用union来存储测试用例?

整个测试的重点是将数组与数组将创建的魔术值进行别名处理。

uint32_t是否保证能够容纳所需的32位/4字节?

基本上是这样。除了32位之外,没有其他保证。它只会在您永远不会遇到的一些非常边缘的架构上失败。

那么赋值{ { 0, 1, 2, 3 } }是什么意思?它将值分配给union,但为什么要使用两个大括号的奇怪标记?

内部括号是为数组而设的。

为什么要检查CHAR_BIT?

因为这是实际的保证。如果这个不出问题,一切都会正常工作。

一条评论提到检查UINT8_MAX会更有用。为什么在这里使用char,当它不能保证是8位宽?

因为事实上现在它总是8位宽的。

为什么不直接使用uint8_t?我找到了这个Google-Devs github的链接。他们并不依赖于这个检查...请有人详细说明一下吗?

还有很多其他选择也可以工作。


谢谢!我想我明白了,当使用数组初始化程序进行初始化时,数据保证按顺序存储在内存中。并且可以通过uint32_t进行寻址后测试字节序。实际上,当我尝试使用宏032_HOST_ORDER时,确实会出现错误。error: invalid suffix '_HOST_ORDER' on integer constant.你有什么想法吗?这里发生了什么? - LastSecondsToLive
是的,第一个字母应该是“O”而不是“零”。 - DigitalRoss

2
初始化有两组大括号,因为内部的大括号初始化了“bytes”数组。所以,“byte [0]”是0,“byte [1]”是1,依此类推。
联合体允许一个“uint32_t”位于与“char”数组相同的字节上,并按照机器的字节顺序进行解释。因此,如果机器是小端的,则“value”的低位字节为0,高位字节为3。相反,如果机器是大端的,则“value”的高位字节为0,低位字节为3。

非常感谢!我现在懂了。然而,当我尝试访问032_HOST_ORDER时,我确实遇到了一个错误 - error: invalid suffix '_HOST_ORDER' on integer constant。您有什么想法这里发生了什么? - LastSecondsToLive
笔误:应该是 O32_HOST_ORDER,而不是 032_HOST_ORDER。标识符不能以数字开头。 - dbush
哦,我真是太蠢了。非常感谢你 - 你刚刚救了我一命。 - LastSecondsToLive

2

{{0, 1, 2, 3}} 是联合体的初始化器,这将导致 bytes 组件被填充为 [0, 1, 2, 3]

现在,由于 bytes 数组和 uint32_t 占用同一空间,因此您可以将相同的值作为本机 32 位整数读取。该整数的值显示了数组是如何洗牌的 - 这实际上意味着您使用的是哪种字节序。

这里只有 3 种常见的可能性 - O32_LITTLE_ENDIANO32_BIG_ENDIANO32_PDP_ENDIAN

至于 char / uint8_t - 我不知道。我认为最好只使用没有检查的 uint_8


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接