g++ 关于字节序的问题*应该*有效吗?

use*_*123 0 c c++ endianness unions bit-fields

认为这应该可行,但我显然错了,但我不知道为什么:-) 假设我有来自网络 0x03 0x02 的以下字节。在我的脑海中,我希望它被转换为小端和以下联合

struct decoded {
    uint16_t opcode : 12;
    uint8_t  unused : 1;
    uint8_t  numRegs : 3;
}

union words {
     decoded a;
     uint8_t byes[2];
}
Run Code Online (Sandbox Code Playgroud)

我希望我可以使用 be16toh(a.opcode) 并得到 0x030 并且 numRegisters 是 0x02。我发现,即使进行字节序转换,我也会得到 0x302 和 0x00 之类的结果,但我不知道为什么:-(

Joh*_*ger 6

为了扩展我的评论,人们倾向于对一般结构布局和特别是位域布局做出很多假设,而这些假设根本不是建立在 C 或 C++ 语言规范的基础上的。详细信息应在适用的应用程序二进制接口 (ABI) 规范中进行描述,但这因体系结构和操作系统而异。

一般来说,您可以信赖的就是

  • 位域将以“纯二进制表示法”存储

  • 位域的存储将在 C 或 C++ 实现选择的“可寻址存储单元”内分配,其大小和对齐要求未指定。

  • 每个 ASU 将包含至少一个完整的位字段

  • 如果所选 ASU 中有足够的空间,则相邻的完整位字段将被打包到同一位字段的相邻位中。

如果一个 ASU 末尾有一些可用空间,但不足以容纳下一个位字段,则位字段是否跨越两个 ASU 是由实现定义的。

未指定位字段在给定 ASU 中出现的顺序。

位域还有其他未指定和实现定义的方面。

但是让我们考虑一个探索特定位域的程序。鉴于这个问题同时被标记为 C 和 C++,这是用 C++ 编写的,但很大程度上是类似 C 的习惯用法:

#include <cstdio>
#include <cstdint>

struct decoded {
    uint16_t opcode : 12;
    uint8_t  unused : 1;
    uint8_t  numRegs : 3;
};

union words {
    decoded a;
    uint8_t byes[2];
};

int main(void) {
    words u;
    u.byes[0] = 0x03;
    u.byes[1] = 0x02;

    printf("structure size: %zu\n", sizeof(decoded));
    printf("opcode: %#06hx;  unused: %#04hhx;  numRegs: %#04hhx\n", u.a.opcode, u.a.unused, u.a.numRegs);
}
Run Code Online (Sandbox Code Playgroud)

在我的 x86-64 Linux 工作站上,其输出是:

structure size: 2
opcode: 0x0203;  unused: 0000;  numRegs: 0000
Run Code Online (Sandbox Code Playgroud)

这显示了有关我的系统的以下信息:

  • 编译器为该结构选择了单个 16 位 ASU。它不能更小,因为它必须容纳 12 位位字段并且是 a 大小的倍数char(在本机上为 8 位)。它并不大,因为整个结构的大小是 16 位。

  • 编译器将opcode成员分配给最低有效的 12 位 (0 - 11)

  • 我们可以得出结论,编译器分配unused给了位 12 和numRegs位 13 - 15。

这是按存储顺序排列的布局:

0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0
L----------- words -----------|
L---------- decoded ----------|
L------------ ASU ------------|
L-- bytes[0] --|--- bytes[1] -|
L----|-|------- opcode -------|
   \   \
    \   +- unused
     +- numRegs 
Run Code Online (Sandbox Code Playgroud)

应该清楚为什么unusednumRegs字段都是 0。

的位opcode001100000010,所以出现了如何解释它的问题?答案是,位模式在左侧填充零,以将其扩展到 a 的位数uint16_t(因为这是位域的声明类型),并从那里以普通(对于本机)方式解释它。由于机器是小端字节序,因此报告的值为 0x0203。

我希望我可以使用 be16toh(a.opcode) 并得到 0x030 并且 numRegisters 是 0x02。

如果位域从左(最重要)到右排列,那么这在机器上是一个合理的结果。我的不是这样的机器,我想你的也不是。