Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用并集和位字段表示寄存器时出现问题 我在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_C++_Struct_Union_Cpu Registers_Bit Fields - Fatal编程技术网

用并集和位字段表示寄存器时出现问题 我在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

用并集和位字段表示寄存器时出现问题 我在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,c++,struct,union,cpu-registers,bit-fields,C++,Struct,Union,Cpu Registers,Bit Fields,因此,我可以访问单个位字段,以及整个寄存器 最初,我将登记簿写为: union { struct { uint16_t coarseX : 5; // bit field type is uint16_t, same as reg type uint16_t coarseY : 5; uint16_t baseNametableAddressX : 1;

因此,我可以访问单个位字段,以及整个寄存器

最初,我将登记簿写为:

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
该错误是由位字段行为引起的,当位字段的类型(例如,ROSSERX)与寄存器reg的类型不同时。 在这种情况下,当我增加一个字段(即Groassex++)时,reg成员被错误地更新,这意味着reg中的位模式没有反映由位字段或位字段表示的模式,因为我在结构中对它们进行了布局。 我知道编译器可以在分配单元中打包位字段,甚至可以插入填充,但是为什么当我更改位字段的类型时,行为会发生变化


有人能解释一下原因吗?

用于位字段的类型是用于内部存储的类型。实际布局完全由实现定义。我假设编译器在“坏”示例中将位字段打包到存储单元uint8\t中,但不允许它们跨越存储单元边界。比如:

    uint8_t coarseX : 5;
// 3 bits remain (out of 8), not enough for coarseY. So these become padding,
// and next storage unit starts here
    uint8_t coarseY : 5;
    uint8_t baseNametableAddressX : 1;
    uint8_t baseNametableAddressY : 1;
// 1 bit remain. Again, too little.
    uint8_t fineY : 3;
    uint8_t unused : 1;
在“好”示例中,16位对于所有位字段都足够了,因此编译器可以按照您需要的方式对它们进行打包。有关更多信息,请参阅


还记得C++中的非活动联盟成员是UB。因此,最好使用一个uint16_t字段和访问器,这些字段和访问器不会阻止类型为POD/triple/standard layout。

用于位字段的类型是用于内部存储的类型。实际布局完全由实现定义。我假设编译器在“坏”示例中将位字段打包到存储单元uint8\t中,但不允许它们跨越存储单元边界。比如:

    uint8_t coarseX : 5;
// 3 bits remain (out of 8), not enough for coarseY. So these become padding,
// and next storage unit starts here
    uint8_t coarseY : 5;
    uint8_t baseNametableAddressX : 1;
    uint8_t baseNametableAddressY : 1;
// 1 bit remain. Again, too little.
    uint8_t fineY : 3;
    uint8_t unused : 1;
在“好”示例中,16位对于所有位字段都足够了,因此编译器可以按照您需要的方式对它们进行打包。有关更多信息,请参阅

还记得C++中的非活动联盟成员是UB。因此,最好使用一个uint16_t字段和访问器,这些字段和访问器不会阻止类型为POD/普通/标准布局。

您自己说过:

我知道编译器可以在分配单元中打包位字段,甚至可以插入填充

这正是正在发生的事情

uint8\u t中有8位。结构中的前两个字段,即roarsex和roarsey,每个字段有5位,无法在内存中的单个字节内连续填充。编译器将粗X存储在第一个字节中,然后必须将粗Y推送到内存中的第二个字节,在粗X和粗Y之间的内存中保留3个未使用的位,以偏移寄存器中的值

接下来的3个字段,粗略、baseNametableAddressX和baseNametableAddressY,总共7位,因此它们适合于第2个字节

但该字节不能保存fineY和unused字段,因此它们被推送到内存中的第三个字节,在baseNametableAddressY和fineY之间的内存中留下1个未使用的位,用于偏移寄存器中的值。寄存器无法访问第三个字节

因此,实际上,您的结构最终会像这样声明:

协会 { 结构 { //字节1 uint8_t粗x:5; uint8_t填充1:3; //字节2 uint8粗度:5; uint8_t baseNametableAddressX:1; uint8_t baseNametableAddressY:1; uint8_t填充2:1; //字节3! uint8精细度:3; 未使用的单元:1; uint8_t填充3:4; }比特; 结构{ uint16\u t reg;//你自己说过:

我知道编译器可以在分配单元中打包位字段,甚至可以插入填充

这正是正在发生的事情

uint8_t中有8位。结构中的前两个字段,即粗X和粗Y,每个字段有5位,不能连续地放入内存中的单个字节中。编译器将粗X存储在第一个字节中,然后必须将粗Y推到内存中的第二个字节,在粗X和粗Y之间的内存中留下3个未使用的位,以偏移内存中的值登记册

接下来的3个字段,粗略、baseNametableAddressX和baseNametableAddressY,总共7位,因此它们适合于第2个字节

但是该字节不能保存fineY和unused字段,因此它们被推到内存中的第三个字节,在baseNametableAddressY和fineY之间的内存中留下1个未使用的位,用于偏移寄存器中的值。并且寄存器无法访问该第三个字节

因此,实际上,您的结构最终会像这样声明:

协会 { 结构 { //字节1 uint8_t粗x:5; uint8_t填充1:3; // 字节2 uint8粗度:5; uint8_t baseNametableAddressX:1; uint8_t baseNametableAddressY:1; uint8_t填充2:1; //字节3! uint8精细度:3; 未使用的单元:1; uint8_t填充3:4; }比特; 结构{
uint16_t reg;//因此,对于将来,我建议不要使用位字段,而是写访问器,并使用位移位和位掩码。位字段填充取决于实现。您使用的编译器、编译器版本和编译器选项是什么?因此,对于将来,我建议不要使用位字段,而是写访问器,并使用位移位和位掩码。位字段添加取决于实现。您使用的编译器、编译器版本和编译器选项是什么?因此,概括一下,如果我使用uint8,分配单元是一个字节,并且由于实现不允许跨字节,它会插入填充。如果我使用uint16,分配单元可能是16位宽的无符号短,并且因为位字段是d在不跨越字节边界的情况下,它们被打包在一起。这就是Visual C++在我的情况下所做的,但是它可以从另一个实现中改变。我是正确的吗?如果我为每个位字段使用不同的数据类型,那会怎么样?@卢卡:是的,当你跨越分配单元之间的边界时,无论它们的大小是什么,都会得到填充。因此,如果我使用UIT88T,分配单元是一个字节,因为实现不允许跨字节之间插入填充。如果使用UTIN 16t,分配单元可能是16位宽的无符号短,并且由于位域不跨越字节边界,所以它们被打包到THOHTEL。这就是Visual C++在我的情况下所做的,但是我t可以从另一个实现中更改。我说得对吗?如果我对每个位字段使用不同的数据类型呢?@Luca是的,每当你跨越分配单元之间的边界时,不管它们的大小,你都会插入填充。