c++ g++关于endianism的问题 * 应该 * 工作吗?

eni9jsuy  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(144)

我 * 认为 * 这应该可以工作,但我显然错了,但我不知道为什么:-)假设我有以下来自网络的字节0x 030 x 02。在我的头脑中,我希望它被转换为littleendian和以下并集

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

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

我本以为可以执行be 16 toh(一个.opcode)并得到0x 030,而numRegisters为0x 02。我发现,即使使用字节序转换,也会得到0x 302和0x 00之类的结果,但我不知道为什么:-(

z0qdvdin

z0qdvdin1#

为了扩展我的评论,人们倾向于对结构布局,特别是位域布局做很多假设,这些假设根本不是建立在C或 C++ 规范上的。细节应该在适用的应用程序二进制接口(ABI)规范中描述,但这随着体系结构和操作系统的不同而不同。
一般来说,你所能依赖的就是

  • 位字段将以“纯二进制记数法”存储
  • 位字段的存储将在由C或C++实现选择的“可寻址存储单元”内分配,其大小和对齐要求未指定。
  • 每个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);
}

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

structure size: 2
opcode: 0x0203;  unused: 0000;  numRegs: 0000

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

  • 编译器为该结构选择了单个16位ASU。它不能更小,因为它必须容纳12位位字段,并且是char大小的倍数(在此计算机上为8位)。它不能更大,因为整个结构的大小为16位。
  • 编译器将opcode成员分配给最低有效的12位(0 - 11)
  • 我们可以得出结论,编译器将X1 M2 N1 X分配给位12,并且将X1 M3 N1 X分配给位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

应该很清楚为什么unusednumRegs字段都是0。
opcode的位数是001100000010,因此问题是如何解释它?答案是位模式用零填充 * 左侧 *,以将其扩展为uint16_t的位数(因为这是位字段的声明类型),并在普通(对于这台机器)离那里很远。因为这台机器是little-endian,所以报告的是0x 0203。
我希望我可以使用be 16 toh(一个.操作码)并获得0x 030,numRegisters为0x 02。
在比特域从左(最重要)到右排列的机器上,这似乎是一个合理的结果,但我的机器不是这样的机器,我猜你的机器也不是。

相关问题