Размер упакованной структуры с битовыми полями - PullRequest
1 голос
/ 20 марта 2020

Рассмотрим этот код:

#include <stdio.h>
#include <stdint.h>

#ifdef __GNUC__
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop))
#endif

PACK(struct S
{
  uint8_t  f0;
  uint32_t f1;
  uint32_t f2 : 17;
});

int main()
{
    printf("%zu\n", sizeof(struct S));
    return 0;
}

Не заданы параметры компилятора.

Вывод:

gcc   (9.2.0): 8
clang (8.0.1): 8
cl    (19.23.28106.4 for x86): 9

Почему в случае cl результат sizeof равно 9?

Что говорит стандарт?

Ответы [ 2 ]

2 голосов
/ 20 марта 2020

Стандарт C C17 6.7.2.1/11 гласит: выделено:

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

Эти "единицы хранения" известны только внутри компилятора и могут иметь разные размеры на разных компиляторах. Концепция битовой / байтовой «упаковки» не поддерживается стандартом и также ведет себя по-разному в зависимости от компилятора.

Ни один из компиляторов, использованных в вашем тесте, не нарушает стандарт C, поскольку это 100% поведение, определяемое реализацией.

1 голос
/ 20 марта 2020

Запросы на упаковку запрашивают, чтобы компилятор упаковывал элементы в структуры без заполнения. Однако они не меняют типы или представления самих членов. Похоже, G CC и Clang используют три байта для представления битового поля из 17 битов, а Microsoft использует четыре, по крайней мере, когда базовый тип равен uint32_t. Таким образом, для этой структуры Clang и G CC упаковывают однобайтовый объект, четырехбайтовый объект и трехбайтовый объект в восемь байтов, в то время как Microsoft упаковывает однобайтовый объект, четырехбайтовый объект, и четырехбайтовый объект на девять байтов.

Это может быть связано с тем фактом, что компилятор Microsoft в основном является компилятором C ++, а C и C ++ по-разному обрабатывают типы битовых полей . В C тип битового поля может быть определен реализацией (стандарт не совсем понятен). В C ++ типом битового поля является его базовый тип.

Мы можем проверить это, рассмотрев переупорядоченную распакованную структуру:

struct S
{
    uint8_t  f0;
    uint32_t f2 : 17;
    uint32_t f1;
};

G CC и Clang show sizeof(struct S) должно быть восемь байтов, что согласуется с трехбайтовым представлением для битового поля. MSV C показывает двенадцать байтов, что соответствует четырехбайтовому представлению для битового поля.

...