Упакованные битовые поля в c-структурах - GCC

Я работаю с structs в c на linux. Я начал использовать битовые поля и "упакованный" атрибут, и я наткнулся на странное поведение:

struct t1
{
    int a:12;
    int b:32;
    int c:4;
}__attribute__((packed));

struct t2
{
    int a:12;
    int b;
    int c:4;
}__attribute__((packed));

void main()
{
    printf("%d\n",sizeof(t1)); //output - 6
    printf("%d\n",sizeof(t2)); //output - 7
}

Почему обе структуры, которые являются точно такими же, принимают различное количество байтов?

Ответ 1

Ваши структуры не являются "точно такими же". У вашего первого есть три последовательных битовых поля, второе - одно битовое поле, (не бит-поле) int, а затем второе поле бит.

Это важно: последовательные (отличные от нуля) битовые поля объединяются в единую ячейку памяти, тогда как поле бит, за которым следует небитовое поле, представляет собой различные ячейки памяти.

В вашей первой структуре есть одна ячейка памяти, вторая - три. Вы можете взять адрес члена b во второй структуре, а не в первой. Доступ к элементу b не выполняется с доступом к a или c во второй структуре, но они выполняются в первую очередь.

Имея небитовое поле (или бит-поле нулевой длины) сразу после того, как член бит-поля "закрывает" его в определенном смысле, последующим будет другое/независимое местоположение/объект памяти. Компилятор не может "упаковать" ваш член b внутри поля бит, как в первой структуре.

Ответ 2

struct t1 // 6 bytes
{
    int a:12; // 0:11
    int b:32; // 12:43
    int c:4;  // 44:47
}__attribute__((packed));

struct t1 // 7 bytes
{
    int a:12; // 0:11
    int b;    // 16:47
    int c:4;  // 48:51
}__attribute__((packed));

Регулярный int b должен быть выровнен по границе байта. Таким образом, перед ним есть прокладка. Если вы положите c рядом с a, это дополнение больше не понадобится. Вероятно, вы должны это сделать, поскольку доступ к целым числам, не равным байтам, таким как int b:32, выполняется медленно.