C中重叠的位字段

时间:2014-10-16 15:02:46

标签: c struct unions bit-fields

我是一名计算机科学专业的学生。

现在,我正在使用C模拟处理器的计算机体系结构项目。

有许多类型的指令,例如

 31     27 26     22 21     17 16                              0
 ---------------------------------------------------------------
|    op   |    ra   |    rb   |             imm17               |
 ---------------------------------------------------------------

 31     27 26     22 21     17 16                7 6 5 4       0
 ---------------------------------------------------------------
|    op   |    ra   |    rb   |       imm10       | m |  shamt  |
 ---------------------------------------------------------------

 31     27 26     22 21                                        0
 ---------------------------------------------------------------
|    op   |    ra   |                  imm22                    |
 ---------------------------------------------------------------

所以,我想制作一个C结构,其中包含与每个元素相对应的位字段,如op,ra等。

起初,我认为我可以使用联合和嵌套结构。

例如,我编写了如下代码:

struct instr_t {
    union {
        uint32_t imm22 : 22;

        struct {
            union {
                uint32_t imm17: 17;
                struct {
                    uint8_t shamt: 5;
                    uint8_t mode : 2;
                    uint16_t imm10 : 10;
                };
            };
            uint8_t rb : 5;
        };
    };

    uint8_t ra : 5;
    uint8_t op : 5;
}

我预计sizeof(struct instr_t)的结果为4,但现实为12。

也许嵌套的结构有一些填充。

所以,这是我的qeustion:

如何实现重叠的C位字段?

是否有人可以推荐更好的方法在C中实现多种类型的指令?

谢谢!

3 个答案:

答案 0 :(得分:4)

位字段成员必须存储在相同的存储单元中才能连续布局:

struct instr_1_t {
    uint32_t imm22 : 17;
    uint32_t rb : 5;
    uint32_t ra : 5;
    uint32_t op : 5;
};

struct instr_2_t {
    uint32_t shamt: 5;
    uint32_t m: 2;
    uint32_t imm10 : 10;
    uint32_t rb : 5;
    uint32_t ra : 5;
    uint32_t op : 5;
};

struct instr_3_t {
    uint32_t imm22 : 22;
    uint32_t ra : 5;
    uint32_t op : 5;
};

union instr_t {
    struct {
        uint32_t pad : 22;
        uint32_t op : 5;
    };
    instr_1_t instr_1;
    instr_2_t instr_2;
    instr_3_t instr_3;
};

static_assert(sizeof(instr_t) == sizeof(uint32_t), "sizeof(instr_t) != sizeof(uint32_t)");

void handle_instr(instr_t i) {
    switch(i.op) {
        //
    }
}

答案 1 :(得分:4)

Maxim给出了正确答案。

我还建议查看这段代码,以了解sizeof instr_t给出的原因12:)

typedef struct s1{
    uint8_t shamt: 5;
    uint8_t mode : 2;
    uint16_t imm10 : 10;
} s_1;

typedef union u1{
    uint32_t imm17: 17;
    s_1 member0;
} u_1;

typedef struct s2{
    u_1 member1;
    uint8_t rb : 5;
} s_2;

typedef union u2{
    uint32_t imm22 : 22;
    s_2 member3;
} u_2;

typedef struct instr_t {
    u_2 member4;
    uint8_t ra : 5;
    uint8_t op : 5;
} s_instr;

int main(int argc, char* argv[])
{
    printf("sizes s_1=%d, u_1=%d, s_2=%d, u_2=%d, s_instr=%d\n", sizeof(s_1), sizeof(u_1), sizeof(s_2), sizeof(u_2), sizeof(s_instr)); 
    printf("uint8_t=%d, uint16_t=%d, uint32_t=%d\n", sizeof(uint8_t), sizeof(uint16_t), sizeof(uint32_t));
    printf("Sizeof instr_t is %d\n", sizeof(s_instr));
}

希望这有帮助!

干杯!

答案 2 :(得分:-1)

位域不可移植。你永远不知道相同的位域定义是否在两个不同的编译器上获得相同的结果。 Bitfields也让我们说多线程程序中有趣的语义。

使用C ++编写一个带有适当内联访问器的类。我的意思是你是一名计算机科学专业的学生,​​你知道C ++,对吗?

如果由于某些疯狂的原因,您的上级要求代码是用C语言编写的,请使用移位和屏蔽操作编写一个带有一个uint32_t成员和单个访问器函数的结构。显然也有内联。