将位字段封装得更加紧密

Packing bitfields even more tightly

本文关键字:字段 封装      更新时间:2023-10-16

派生类中的位字段有问题。

使用g++编译器,您可以将__attribute__((packed))分配给一个类,它将打包位字段。所以

class A
{
  public:
    int one:10;
    int two:10;
    int three:10;
} __attribute__ ((__packed__));

仅占用4个字节。到目前为止,一切都很好
但是,如果你继承了一个类,比如这个

class B
{
  public:
    int one:10;
    int two:10;
} __attribute__ ((__packed__));
class C : public B
{
  public:
    int three:10;
} __attribute__ ((__packed__));

我希望与上面的类A具有相同内容的类C也具有相同的布局,即占用4个字节。然而,C占用了5个字节。

所以我的问题是,我做错了什么吗?如果是,怎么办?或者这是编译器的问题?一个疏忽,一个真正的错误?

我试过在谷歌上搜索,但除了Linux和Windows(编译器试图模仿MSVC)之间的区别之外,我什么都没想出来,我对此不感兴趣。这只是在Linux上。

我认为问题出在B上,它不可能是2.5字节。它必须至少为3个字节。

理论上,派生类可能被允许重用基类中的填充,但我从未见过这种情况发生。

想象一下,您所要求的可能的。这可能会产生什么副作用或问题?让我们看看你的一个具体例子。还假设一个具有1字节内存对齐的32位体系结构。

class A中有20个连续的位,您可以通过类的成员onetwo进行寻址。这对你来说是一个非常方便的称呼,人类。但是编译器是怎么做到的呢?它使用掩码并将位移位到位置,将这些位移到正确的位置。

到目前为止,一切都很好,似乎足够简单和安全。

再添加10位。比方说,有一个非常聪明的编译器,它可以让你把多余的10位压缩到一个已经使用过的32位单词中(它们非常适合,不是吗?)。

麻烦来了:

A* derived = new B; // upcast to base class
derived->one = 1;
derived->two = 2;
// what is the value of derived->three in this context?
// Especially taking into account that a compiler is free to do all sorts
// of optimizations when generating code for class A

由于上述原因,类class A的成员和class B的成员使用不同的和可单独寻址的内存位置,导致这10位"溢出"到下一个可寻址内存位置——下一个字节。

当您考虑多重继承时,还会遇到更多的麻烦——在派生类中排列位的真正方法是什么?