Как компилятор C выделяет структурную память для структур, определенных битовым полем? - PullRequest
0 голосов
/ 03 мая 2019

Я определил структуру объединения следующим образом.

typedef union {
      struct {
         uint8_t id : 7;
         uint8_t age : 1;
         uint16_t index : 16; 
         uint8_t class : 4;
         uint8_t reserved : 4; 
      } fields;

   uint32_t value[1];

} entry_t;

В моей основной функции я передаю некоторые данные, используя член объединения "value", а затем распечатываю данные, используя структуру "fields". Я также распечатываю размер структуры.

int main()
{
    entry_t entry; 

    entry.value[0] = 0xACEDBEEF;

    printf("entry.fields.id = %x \n", entry.fields.id);
    printf("entry.fields.age = %x \n", entry.fields.age);
    printf("entry.fields.index = %x \n", entry.fields.index);
    printf("entry.fields.class = %x \n", entry.fields.class);
    printf("entry.fields.reserved = %x \n", entry.fields.reserved);

    printf("sizeof(entry): %d \n", sizeof(entry));

    return 0;
}

Вот что я вижу на консоли:

entry.fields.id = 6f 
entry.fields.age = 1 
entry.fields.index = aced 
entry.fields.class = d 
entry.fields.reserved = f 
sizeof(entry): 8 

Мои вопросы: 1) Почему я не вижу entry.fields.index как "EDBE". Это то, что я ожидал. 2) Почему sizeof (запись): 8? Я ожидал, что это будет 4

Интересно, если я изменю структуру так, чтобы "fields.index" определялся следующим образом (uint32_t вместо uint16_t):

uint32_t index : 16; 

Тогда это работает так, как я ожидал (т.е. entry.fields.index = 0xEDBE и sizeof (entry) = 4).

Почему компилятор обрабатывает 2 случая по-разному?

1 Ответ

0 голосов
/ 03 мая 2019

Две силы в игре - это порядок байтов и выравнивание.Похоже, ваш компьютер хранит данные в формате с прямым порядком байтов, то есть младший байт хранится по младшему адресу.Таким образом, ваш инициализатор был эффективен:

entry.fields.id = (0xef >> 1) & 0x7f;
entry.fields.age = (0xef >> 7) & 1;
entry.fields.index = 0xaced;
entry.fields.class = random stack data & 0xf;
entry.fields.reserved = random stack data & 0xf;

Поскольку entry.fields.index является uint16_t, для вставки байта заполнения или выравнивания требовалось, чтобы последовательность битовых полей была прервана.Ваши предыдущие поля (id, age) вызывали смещение для 16-битного типа, компилятор исправил это, и ваш '0xbe' исчез.

Если вы слегка измените свое определение:

struct {
    unsigned id: 7, age:1, index :16, class:4, reserved:4;
} fields;

Вы можете увидеть что-то ближе к тому, что вы ожидали.Обратите внимание на удаление "uint32_t", это немного претенциозно для битовых полей;без вреда, просто уродливо читать.

В форме вашего оригинала вы предоставляете ограничения выравнивания для битовых полей: id, age, class, reserved не может охватывать 8-битную границу.Индекс не может охватывать 16-битную границу.Когда он доходит до точки выделения для индекса, он должен ввести 8-битное заполнение для соответствия ограничению выравнивания.

В более короткой форме я дал id, age, index, class, reserved не может пересечь 32-битную границу;то, что компилятор может вместить в одно слово.

...