Выравнивание битовых полей (gcc) в структуре

Прочел статью и решил применить упаковку, но оказалось всё не так просто, как описано.

Есть такая структура

struct info {
  VipsImage  *in, *out;
  unsigned    buff_len:24;
  unsigned    name_len:8;
  unsigned    height:10;
  unsigned    w:10;        
  unsigned    h:10;  
  unsigned    isWebp:1;    
  unsigned    isBig:1;
};

Весит на 64-битной архитектуре 24 байта. Стоит переставить мембер name_len ниже под десятибитные мемберы, как структура начинает весить уже 32 байта (просто мистика, сколько мусора добавляет компилятор при одинаковых значениях в битовых полях, но не корректно размещённых в структуре) первый отступ я нашел, а второго пока что нет, выглядит все это дело так (переставил name_len и добавил 30 бит padding_1 для того что бы попытаться понять как именно выравнивает компилятор в этом случаи, в итоге структура осталась весить 32 байта и как я понял это ещё не весь мусор...) :

struct info {
  VipsImage  *in, *out;
  unsigned    buff_len:24;
  unsigned    height:10;
  unsigned    padding_1:30; 
  unsigned    w:10;        
  unsigned    h:10;
  unsigned    name_len:8;
  unsigned    isWebp:1;    
  unsigned    isBig:1;
};

Пытался разместить как описано в статье по принципу (расположить элементы структуры по уменьшению размера), но с битовыми полями оказалось так не проходит. Как именно происходит в этом случаи выравнивание битовых полей ? и почему компилятор так странно выравнивает битовые поля по размеру указателя в структуре ?

P.S. Версия компилятора gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)


Ответы (1 шт):

Автор решения: Alexey Nikolaev

В статье по вашей ссылке описано, как выравниваются битовый поля - по машинному слову (32-бита), объявленному вами как unsigned.

struct info {
    VipsImage  *in, *out; // 4 слова (64 + 64)
    
    unsigned    buff_len:24;
    unsigned    name_len:8; //эти два поля влезают в 32 бита - 1 слово
    
    unsigned    height:10;
    unsigned    w:10;        
    unsigned    h:10;  
    unsigned    isWebp:1;    
    unsigned    isBig:1; // эти поля влезают в еще одно слово (32-бита)
};
// итого 64 + 64 + 32 + 32 = 192 (24 байта)

struct info {
    VipsImage  *in, *out; // 4 слова (64 + 64)
    
    unsigned    buff_len:24;    // под это поле зарезервируется слово (32-бита) потому что следующее поле не влезает в слово
    unsigned    height:10;      // под это поле зарезервируется слово (32-бита) потому что следующее поле не влезает в слово
    unsigned    padding_1:30;   // под это поле зарезервируется слово (32-бита) потому что следующее поле не влезает в слово
    
    unsigned    w:10;        
    unsigned    h:10;
    unsigned    name_len:8;
    unsigned    isWebp:1;    
    unsigned    isBig:1;        // под эти поля зарезервируется слово (32-бита), но использоваться будут только 30-бит
};
// итого 64 + 64 + 32 + 32 + 32 + 32 = 256 (32 байта)
→ Ссылка