Упаковка битовых полей еще плотнее

У меня проблема с битовыми полями в производных классах.

С помощью компилятора 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__));

Я ожидаю, что класс C, который имеет то же содержимое, что и класс A, описанный выше, также будет иметь такой же макет, то есть занимать 4 байта. Однако C оказывается 5 байтов.

Итак, мой вопрос, я делаю что-то не так, и если да, то что? Или это проблема с компилятором? Недосмотр, реальная ошибка?

Я пробовал гуглить, но ничего не придумал, кроме разницы между Linux и Windows (где компилятор пытается эмулировать MSVC), которая мне не интересна. Это только на Linux.

2 ответа

Решение

Я полагаю, что проблема с B, который не может легко быть 2,5 байта. Должно быть не менее 3 байтов.

Теоретически, производному классу может быть разрешено повторное использование отступов из базового класса, но я никогда не видел, чтобы это произошло.

Представьте на секунду, что то, о чем вы просите, возможно. Каковы будут возможные побочные эффекты или проблемы этого? Давайте посмотрим на конкретный пример, который у вас есть. Также предположим, что 32-битная архитектура с 1-байтовым выравниванием памяти.

20 последовательных битов class A что вы можете обратиться через членов класса one а также two, Это очень удобное для вас обращение, человек. Но что делает компилятор, чтобы это произошло? Он использует маски и биты, чтобы расположить эти биты в правильных местах.

Пока все хорошо, кажется простым и достаточно безопасным.

Добавляем еще 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 битов "пролиться" в следующую адресуемую ячейку памяти - следующий байт.

Еще больше проблем возникает, когда вы рассматриваете множественное наследование - каков единственный верный способ размещения битов в производном классе?

Другие вопросы по тегам