在C语言中访问位域

4

我有一个结构体,对应硬件的寄存器。

typedef unsigned int uint32;
typedef union A_u{
    uint32 words[4];
    struct {

        uint32 a : 2;
        uint32 b : 3;
        uint32 unused : 27;

        uint32 c : 2;
        uint32 d : 3;
        uint32 unused0 : 27;

        uint32 unused1 : 1;
        uint32 e : 1;
        uint32 f : 1;
        uint32 g : 1;
        uint32 h : 1;
        uint32 i : 1;
        uint32 unused2 : 26;

        uint32 reserved6 : 32;
       }s;
}A_t;

main()
{

    A_t obj;
    uint32 val = 1;

    memset(&obj, 0, sizeof(A_t));
    //fills data
    read_data(&obj);

    printf("0x%x\n", obj.words[2]);
    printf("obj.s.h = %d\n", obj.s.h);

}

输出结果为

0x80000000
obj.s.h = 1.

虽然第三个字是0x80000000,但是obj.s.h显示为1,我不理解这个现象。我在powerpc上运行此代码,其中第一个位域是最高有效位。


1
看看这个输出:printf("sizeof(obj) is %d bytes\n", sizeof(obj)); - 位域的打包方式非常依赖于编译器。 - Roddy
sizeof(obj) 是 16,即 4 个 32 位字。 - user405369
你的%d应该是%u,因为位域是无符号的,尽管这可能不是问题的解决方案。 - tinman
1个回答

4
编译器在与结构体、联合体和其他复杂数据结构的成员对齐内存位置方面有相当大的自由度。
我曾经遇到过神秘的运行时错误,这是由于源代码中的一部分将结构体视为一种方式,而另一部分将其视为另一种方式导致的对齐边界问题。
C语言基本上使用结构体作为模板来覆盖内存区域,如果不同的文件以不同的方式看待结构体,则代码的一部分计算结构体成员的地址偏移量与另一部分不同。
一些编译器会提供一个pragma,允许您指定一个打包操作,以便您可以指定结构体成员的对齐边界。
为了保持与硬件的兼容性,最好的方法似乎是使用无符号字符数组,因为这样您可以索引到无符号字符数组元素,然后使用位运算符选择其中的一个字节。然后,您可以在各种访问函数、类、宏等中包装无符号字符数组,以操作您想要读取和写入的实际数据。使用无符号字符数组也更具可移植性。
位域似乎更多地是一种应用程序以更节省内存的方式维护指示器和标志的方法,但对于与应用程序之外的实体(硬件或软件)进行通信并不太有用。而且,根据编译器和应用程序运行的硬件,使用位域可能会引入一些小的运行时惩罚,因为位域被访问、打包和解包。

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