我正在用 C++ 编写一个 NES 模拟器,我遇到了一个使用位字段来表示寄存器的问题,这导致了一个非常讨厌的错误。我将内部地址寄存器表示为:
union
{
struct
{
uint16_t coarseX : 5; // bit field type is uint16_t, same as reg type
uint16_t coarseY : 5;
uint16_t baseNametableAddressX : 1;
uint16_t baseNametableAddressY : 1;
uint16_t fineY : 3;
uint16_t unused : 1;
} bits;
uint16_t reg;
} addressT, addressV; // temporary VRAM adddress register and VRAM address register
所以我可以访问单个位域和整个寄存器。
最初我将寄存器写为:
union
{
struct
{
uint8_t coarseX : 5; // bit field type is uint8_t, reg type is uint16_t
uint8_t coarseY : 5;
uint8_t baseNametableAddressX : 1;
uint8_t baseNametableAddressY : 1;
uint8_t fineY : 3;
uint8_t unused : 1;
} bits;
uint16_t reg;
} addressT, addressV; // temporary VRAM adddress register and VRAM address register
当位字段的类型(例如粗略X)与寄存器(reg)的类型不同时,该错误是由位字段行为引起的。
在这种情况下,当我增加一个字段(即粗略X++)时,reg 成员被“错误地”更新,这意味着 reg 内的位模式没有反射(reflect)位字段(或我铺设的位字段)表示的模式在结构中取出它们)。
我知道编译器可以在“分配单元”中打包位字段,甚至可以插入填充,但是为什么当我更改位字段的类型时行为会发生变化?
有人可以解释为什么吗?
最佳答案
你自己说的:
I know that the compiler can pack bit fields inside "allocation units", and may even insert padding, ...
这正是正在发生的事情。
uint8_t
里面有 8 位。结构中的第一个两个字段 coarseX
和 coarseY
,每个有 5 位,不能在内存中的单个字节内连续放置。编译器存储 coarseX
在第一个字节中,然后必须推 coarseY
到内存中的第 2 个字节,在 coarseX
之间的内存中留下 3 个未使用的位和 coarseY
这抵消了您在寄存器中的值。接下来的 3 个字段,
coarseY
, baseNametableAddressX
和 baseNametableAddressY
,总共 7 位,所以它们适合第二个字节。但是那个字节不能容纳
fineY
和 unused
字段,因此它们被推送到内存中的第三个字节,在 baseNametableAddressY
之间的内存中留下 1 个未使用的位和 fineY
这抵消了您在寄存器中的值。并且寄存器不能访问第三个字节!所以,实际上,您的
struct
最终表现得好像你已经这样声明了它: union
{
struct
{
// byte 1
uint8_t coarseX : 5;
uint8_t padding1 : 3;
// byte 2
uint8_t coarseY : 5;
uint8_t baseNametableAddressX : 1;
uint8_t baseNametableAddressY : 1;
uint8_t padding2 : 1;
// byte 3!
uint8_t fineY : 3;
uint8_t unused : 1;
uint8_t padding3 : 4;
} bits;
struct {
uint16_t reg; // <-- 2 bytes!
uint8_t padding4; // <-- !
}
} addressT, addressV; // temporary
使用
uint16_t
而不是 uint8_t
,您不会遇到添加额外付费的问题,因为为寄存器分配了足够的位来保存您定义的所有位。
关于c++ - 表示带有 union 和位域的寄存器的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60677299/