如何用一个非常奇怪的位域排列构造一个C结构体/联合体?

webghufk  于 2023-08-03  发布在  其他
关注(0)|答案(4)|浏览(121)

我有以下内存布局(伪代码):

struct {
    union {
        fieldA : 45;
        struct {
            fieldB1  :  12;
            fieldB2  :  33;
        }
    }
    fieldC : 19;
}

字符串
即,场A的存储器有时可用于其它目的(场B1和B2)。我希望这个结构体尽可能的压缩,即。64bit大小。
似乎无论我做什么(例如打包属性),联合总是在fieldC(当然也是填充的)之前填充3位以获得48位(6字节)。

ccgok5k5

ccgok5k51#

如何用一个非常奇怪的位域排列构造一个C结构体/联合体?
64 bit大小。
使用一个typedef或一个结构体,其中包含一个uint64_t。使用位域操作为每个字段编写getter和setter。

eaf3rand

eaf3rand2#

作为一种可能的解决方法,您需要复制一些字段,并使用 all 字段创建两个结构的联合:

struct S {
    union {
        struct {
            uint64_t fieldA : 45;
            uint64_t fieldC : 19;
        } a;
        struct {
            uint64_t fieldB1 : 12;
            uint64_t fieldB2 : 33;
            uint64_t fieldC : 19;
        } b;
    };
};

字符串

ulydmbyx

ulydmbyx3#

一般不使用位域。当然不是在您希望可移植的代码中。它们的语义定义比外行人所认为的要松散得多,而且它们在很多方面都令人惊讶。对于一些潜在用途重要的方面是未指定的或实现定义的,并且在实现之间确实变化。

然而,你可以依赖于除了位域之外的每个对象,使其具有由一个或多个(完整)字节组成的连续序列的表示(C23 6.2.6.1/2)。* 假设你的系统的字节是8位宽,你不能使structunion的表示正好包含45位,因为45不是8的倍数。在您的例子中,内部结构体的大小至少为 * 6个字节,因此包含它的联合体也必须至少有这么大。外部结构体包含一个需要19位的额外成员,因此整个结构体的大小必须至少为6 + 4 = 10字节。
我的第一个建议是我的领导:不要使用位域。例如,在一个示例中,

struct foo {
    union {
        uint64_t fieldA;
        struct {
            uint16_t fieldB1  :  12;
            uint64_t fieldB2  :  33;
        };
    };
    uint32_t fieldC;
};

字符串
当然,这并不能实现将其打包成64位的目标,但C并没有定义任何方法来确保结构或联合的这种打包。而且,这已经依赖于实现定义的行为,关于位域成员可能具有哪些类型说明符。
你可以考虑一个结构体的联合,比如@someprogrammerdude推荐的。但是只要64位就足够了,你也可以考虑一个普通的打包整数,也许有支持的宏或函数:

typedef uint64_t my_fields;

#define FIELDA(mf) ((mf) >> 19)
#define SET_FIELDA(mf, v) do { \
    my_fields *temp = &(mf); \
    *temp = (*temp & (~(uint64_t)0 >> 45)) | (((uint64_t) (v)) << 19); \
} while (0)
// ...


由于您表达了可能需要灵活处理数据结构更改的一些担忧,因此使用宏或函数 Package 访问并抽象数据类型可以提供很大的灵活性。

  • 这似乎包括C23位精确的整数类型,所以我猜在许多情况下这些将包括填充位。
zpgglvta

zpgglvta4#

在C编程语言的范围内,struct s和union s实际上并不适合创建精确到位级别的内存布局。即使使用特定于编译器的 packing 扩展/杂注,您仍然可能最终会填充和对齐元素以满足实现的某些要求。在标准C中进行位级精确访问的唯一可靠方法是通过(数组)原语类型和位操作来屏蔽和提取/插入所需的值。然后还有整个endianess问题,我在这里懒得忽略它。
在你的特殊情况下,你可以这样做:

/* using a struct for encapsulation, to get some degree of static typing */
struct mybitfieldtype { uint64_t _; };

#define MYBITFIELD_ELEMENT(name, W, S) \
static inline uint64_t \
    mybitfieldtype_get_##name( struct mybitfieldtype const v ) \
        { return (v._ >> (S)) & ((uint64_t)1<<(W))-1; } \
\
static inline struct mybitfieldtype \
    mybitfieldtype_set_##name( struct mybitfieldtype const v, uint64_t const x ) \
        { return (struct mybitfieldtype) \
            { (v._ & ~(((uint64_t)1<<(W))-1<<(S))) \
            | (x   &  (((uint64_t)1<<(W))-1))<<(S) }; }

MYBITFIELD_ELEMENT(fieldA,  45,  0)
MYBITFIELD_ELEMENT(fieldB1, 12,  0)
MYBITFIELD_ELEMENT(fieldB2, 33, 12)
MYBITFIELD_ELEMENT(fieldC,  19, 45)

字符串

相关问题