Я немного озадачен тем, как следующий код размещается в памяти:
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».
Вы пропустили упакованные атрибуты в свой анонимный союз. Рассмотрим этот пример:
#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
Атрибут фасованный немного странный, я всегда пытаюсь проверить каждую возможную комбинацию, чтобы получить правильный результат.
Ну, во-первых, __attribute__((__packed__))
является расширением gcc, поэтому стандарт не имеет ничего сказать о том, что может или не может произойти с этим.
В целом, однако, компилятору разрешено вставлять любые отступы, которые он считает подходящими, между битовыми полями. В частности, я видел компиляторы, которые дали это:
struct
{
short a : 8;
int b : 8;
}
выровняет b на 32-битной границе.
По сути, если вы используете битовые поля, вы сами по себе. Там нет гарантии порядка полей или отступов. Единственная гарантия, что у вас есть, это размер битового поля.
Вопрос помечен как 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
, он всегда будет отображаться в ближайший целочисленный тип, то есть целое число.