能否将一个类型的位域转换为另一个具有相同总位数的位域?

5

请问是否有可能使用C90编译器将一种类型的位域赋值给另一种类型?

以下是位域的结构体:

typedef struct{
    unsigned int ERA:2;
    unsigned int destar:2;
    unsigned int srcar:2;
    unsigned int opcode:4;
    unsigned int grp:2;
    unsigned int dumy:3;
} Command;
typedef struct{
    unsigned int cell:15;
} Word;

以下是主程序:

int main(int argc, const char * argv[]){
    Word word;
    Command cmd;
    cmd.grp = 2;
    word = cmd;
}

假设我有这15个位元:

我们按照以下顺序排列它们:

命令:

Dumy |    grp | opcode |   srcar | destar | ERA
 0 0 0   0 1    0 0 0 1     10      0 0     0 0

单词:

 |          word              |
000000000000000

目标是使单词等于整个命令(word = command),所以我们会看到这样的结果:

|           word            |
000010001100000

4
为什么被投票为“不清晰”?它非常明确。OP正在询问两个位域的类型是否兼容。 - Eugene Sh.
word = cmd; 将一个结构变量分配给不兼容的变量。word.cell = cmd.ERA; 将一个位域分配给另一个位域。没有进行强制转换。 - chux - Reinstate Monica
但是故事的结尾,它们两个都是占用了大约8个字节的整数... 为什么它不能工作呢? - BananaBuisness
都不占用8个字节。有很多原因不能工作:对齐、打包、填充、别名。 - chux - Reinstate Monica
当然会!尝试使用Sizeof(),你就会看到。 - BananaBuisness
3个回答

3
您可能需要使用联合体(union)来实现此功能:
typedef union {
    struct {
        //unsigned int:1;        // padding bit either here...
        unsigned int ERA:2;
        unsigned int destar:2;
        unsigned int srcar:2;
        unsigned int opcode:4;
        unsigned int grp:2;
        unsigned int dumy:3;
        //unsigned int:1;         // ...or here, depending on how you want to read it
    } cmd;
    uint16_t word;
} Cmd;

int main(int argc, const char * argv[]){
    Cmd cmd;
    cmd.cmd.grp = 2;
    printf("word=%u\n", cmd.word);
}

好主意!谢谢你!但我还想看到一个不包括union的答案。 - BananaBuisness
为什么要使用填充? - BananaBuisness
@DeBanana,指定了15个位,因此编译器还需要放置一个。您可以使用未命名的位字段作为填充,以告诉编译器您期望它放置在哪里。根据您的示例,我猜想您希望它位于开头。 - dbush

1

由于位域布局取决于编译器,我不确定你期望的是什么。
以下代码片段可能包含未定义的行为,但我猜这就是你要找的。

Word word;
Command cmd;
cmd.grp = 2;

union
{
    Word word;
    Command cmd;
} u;

u.cmd = cmd;
word = u.word;

0

如果位域是由编译器从位0分配的,那么“word”和“cmd”都将占用位0:14,这是合理的。

如果位域是由编译器从位15(或者可能是位31,在大多数DSP处理器编译器中)分配的,那么“word”和“cmd”都将占用位15:1(或者32位字的情况下为31:17)。

因此,避免添加另一个位来完成编译器分配的字大小的位域结构似乎是可以的。

但是,这样做是一个好习惯。

问题可能会出现在编译器的“word”是一个字节的情况下。有些微控制器的字是一个字节。在这种情况下,联合定义可能会导致错误。

typedef union {
    struct {
        unsigned int ERA:2;
        unsigned int destar:2;
        unsigned int srcar:2;
        unsigned int opcode:4; // When the compiler see that the opcode
                               // bit field is to big to be allocated in the
                               // first byte, The compiler might allocate 
                               // opcode at the first allocation in the
                               // second byte that is allocated to this 
                               // union.
        unsigned int grp:2;
        unsigned int dumy:3;
    } cmd;
    uint16_t word;
} Cmd; 

在这种情况下,联合应该以这种方式编写。
typedef union {
    struct {
        unsigned int ERA:2;
        unsigned int destar:2;
        unsigned int srcar:2;
        unsigned int Spare1:2; 
        unsigned int opcode:4; 
        unsigned int grp:2;
        unsigned int dumy:2; // Assuming that dummy is also a padding
    } cmd;
    uint16_t word;
} Cmd; 

要注意的是,捕捉此类错误的方法很简单,只需查看处理器的内存以及被声明为该联合类型的变量(或常量)的内存分配情况。我们应该逐个更改位域,并观察这种变化对物理内存的影响。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接