我 * 认为 * 这应该可以工作,但我显然错了,但我不知道为什么:-)假设我有以下来自网络的字节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之类的结果,但我不知道为什么:-(
1条答案
按热度按时间z0qdvdin1#
为了扩展我的评论,人们倾向于对结构布局,特别是位域布局做很多假设,这些假设根本不是建立在C或 C++ 规范上的。细节应该在适用的应用程序二进制接口(ABI)规范中描述,但这随着体系结构和操作系统的不同而不同。
一般来说,你所能依赖的就是
如果在一个位字段的末尾有一些空间可用,但不足以容纳下一个位字段,则位字段是否将跨越两个ASU是由实现定义的。
未指定位字段在给定ASU中出现的顺序。
位字段还有其他未指定和实现定义的方面。
但是让我们考虑一个探索特定位域的程序,考虑到这个问题同时被标记为C和C++,这个程序是用C++编写的,但在很大程度上使用了类似C的语言:
在我的x86-64 Linux工作站上,其输出为:
这将显示有关我的系统的以下信息:
char
大小的倍数(在此计算机上为8位)。它不能更大,因为整个结构的大小为16位。opcode
成员分配给最低有效的12位(0 - 11)下面是按照存储顺序排列的布局:
应该很清楚为什么
unused
和numRegs
字段都是0。opcode
的位数是001100000010
,因此问题是如何解释它?答案是位模式用零填充 * 左侧 *,以将其扩展为uint16_t
的位数(因为这是位字段的声明类型),并在普通(对于这台机器)离那里很远。因为这台机器是little-endian,所以报告的是0x 0203。我希望我可以使用be 16 toh(一个.操作码)并获得0x 030,numRegisters为0x 02。
在比特域从左(最重要)到右排列的机器上,这似乎是一个合理的结果,但我的机器不是这样的机器,我猜你的机器也不是。