Выравнивание битовых полей внутри союзов

Я немного озадачен тем, как следующий код размещается в памяти:

struct Thing
{
union
{
unsigned value:24;
uint8_t bytes[3];
};
Thing(int v)
:value(v)
{}

void foo()
{
printf("Thing %p value=%d !\n", this, value);
}

} __attribute__((__packed__));

В gcc 3.3, 4.3 или 4.6 в Linux (без каких-либо особых опций, о которых я могу думать — только «-Wall -g» в 4.6), размер структуры всегда равен 4:

$ pahole ./union
struct Thing {
union {
unsigned int               value;                /*           4 */
unsigned char              bytes[3];             /*           3 */
};
[...]

У нас был похожий код, где у нас было значение без знака: 24 в структуре, и кто-то добавил объединение и непреднамеренно увеличил размер структуры с 3 до 4 байтов.
То же самое происходит, если я пытаюсь определить объединение как «упакованное» — размер по-прежнему равен 4. Это поведение в соответствии со спецификацией C ++? Каково было бы объяснение?

позднее отредактируйте: заменил «C spec» на «C ++ spec».

4

Решение

Вы пропустили упакованные атрибуты в свой анонимный союз. Рассмотрим этот пример:

#define PACKED __attribute__((__packed__))
struct S1 { unsigned value:24; } PACKED ;
struct S2 { union { char a[3]; unsigned value:24; };  } PACKED ;
struct S3 { union { char a[3]; unsigned value:24; } PACKED ;  };int main() {
std::cout << sizeof(S1) << std::endl;
std::cout << sizeof(S2) << std::endl;
std::cout << sizeof(S3) << std::endl;
}

Выход:

3
4
3

Атрибут фасованный немного странный, я всегда пытаюсь проверить каждую возможную комбинацию, чтобы получить правильный результат.

1

Другие решения

Ну, во-первых, __attribute__((__packed__)) является расширением gcc, поэтому стандарт не имеет ничего сказать о том, что может или не может произойти с этим.

В целом, однако, компилятору разрешено вставлять любые отступы, которые он считает подходящими, между битовыми полями. В частности, я видел компиляторы, которые дали это:

 struct
{
short a : 8;
int b : 8;
}

выровняет b на 32-битной границе.

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

0

Вопрос помечен как C ++, но вы упомянули C spec. AFAIK, есть разница между C и C ++ в этом отношении.
В C битовые поля могут быть только целочисленного типа, в то время как C ++ допускает любой целочисленный тип.

Поскольку битовые поля отображаются в целочисленные типы, я ожидаю, что размер битового поля всегда равен 1, 2 или 4.

Следовательно, это даст вам размер 3:

struct Thing
{
union
{
// without 'short' int would be assumed -> 4 bytes
unsigned short value:15; // -> maps to short -> 2 bytes
uint8_t bytes[3];        // -> 3 bytes, so the whole union is 3 bytes long
};
}

С value:24, он всегда будет отображаться в ближайший целочисленный тип, то есть целое число.

0
По вопросам рекламы [email protected]