更紧凑地打包位域

3
我在派生类中遇到了位域(bitfields)的问题。
使用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__));

我期望class C和class A有一样的内容,布局也应该相同,即占用4个字节。然而,实际情况是C占用了5个字节。所以我的问题是,我是否做错了什么,如果是,是什么?或者这是编译器的问题?是一个疏忽,还是一个真正的错误?我试着搜索了一下,但并没有找到什么有用的信息,除了Linux和Windows之间的差异(编译器试图模拟MSVC),但我对此不感兴趣。这只是在Linux上发生的事情。

我猜这可能根本不可能,但让我们看看别人怎么说。 - Konrad Rudolph
假设你有 C c; B &b1 = c; /* ... */ B b2; /* ... */ b1 = b2;. 采用你建议的打包方式后,最后这个赋值会变得更加复杂。 - user743382
@hvd 怎么样?对位成员的赋值需要进行位操作,那么为什么不在对类的赋值时也使用位操作呢? - Konrad Rudolph
@hvd 为什么?对位域的赋值总是很复杂的;总会有位移操作。我不明白为什么这次会有所不同。 - Mr Lister
但是 b1 = b2; 不是指位域,它复制整个类。这将是一个非常特殊的类,其中该类是 POD,但复制类并不仅仅是复制字节。 - user743382
2个回答

0

想象一下,你所要求的可能实现的。那么可能会有什么副作用或问题呢?让我们看一个具体的例子。同时假设使用32位架构和1字节内存对齐。

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位“溢出”到下一个可寻址的内存位置-下一个字节。

当考虑多重继承时,更麻烦的是如何在派生类中安排位的“唯一真实方式”?


1
除了多重继承之外,编译器已经非常智能了。你的例子可以工作;也就是说,在赋值时,父类中未使用的位保持不变。为什么不呢?已经有一些AND和OR操作无法优化掉了;使用“松散”的位掩码不会帮助提高性能。 - Mr Lister

0

我认为问题出在 B 上,它不能轻易地变成 2.5 个字节。它至少要有3个字节。

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


不知怎么的,我还是期待它会发生。从所有意图和目的来看,A和C具有相同的功能。 - Mr Lister
我想知道剩余的位会发生什么。编译器是否会自动扩大其中一个字段以填充整个字节?我得测试一下。 - Mr Lister
剩余的位只是剩余的,没有被使用。如果你形成一个B的数组,B b[10];,那么sizeof(B)必须是一个整数(否则你无法得到b[1]b[2]的地址)。在创建C时,编译器可能允许使用B中的这个剩余空间,但这很可能不是编译器编写者关注的常见用例。他们有很多其他事情要担心,可能会有更大的回报(比如新的C++11特性:-)。 - Bo Persson
我的使用情况是我需要创建一个相当大的数组,希望能够节省字节。哦,好吧。 - Mr Lister

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