Структура с размером битовых полей

Я попытался использовать структуру с битовыми полями разного размера. Общее количество используемых битов равно 64. Однако, когда я проверяю размер структуры, я получаю 11 вместо ожидаемого 8. Пытаясь разложить структуру, я увидел, что разница исходит из поля день. Если я упаковываю каждый бит, чтобы получить 8-битные пакеты, поле день упаковывается между «концом» месяц и «началом» час. Я не знаю, хороший ли это подход. Может ли кто-нибудь объяснить мне это?

typedef unsigned char uint8_t;

typedef struct frameHeader_t
    {
        uint8_t encryption       : 2;
        uint8_t frameVersion     : 2;
        uint8_t probeType        : 4;
        uint8_t dataType         : 5;
        uint8_t measurePeriod    : 3;
        uint8_t remontePerdiod   : 4;
        uint8_t nbrMeasure       : 2;
        uint8_t year             : 7;
        uint8_t month            : 4;
        uint8_t day              : 5;
        uint8_t hour             : 5;
        uint8_t minute           : 6;
        uint8_t second           : 6;
        uint8_t randomization    : 5;
        uint8_t status           : 4;

    }FrameHeader;

int main()
{

    FrameHeader my_frameHeader;
    printf("%d\n", sizeof(FrameHeader));
    return 0;
}

Вам, вероятно, понадобится #pragma pack или что-то подобное (в зависимости от вашего компилятора, точнее, препроцессора компилятора). Поле year «приземляется» на два байта, поэтому, скорее всего, оно дополняется компилятором. Впоследствии некоторые поля, которые следуют за ним, также «приземляются» на два байта, поэтому применяется большее заполнение.

goodvibration 26.06.2019 16:09

Проблема вот в чем: "... ожидаемая 8". Нет ожидаемого размера битового поля. Их нельзя надежно использовать для отображения памяти.

Lundin 26.06.2019 16:11

В чистом C нет никаких гарантий относительно размещения структуры в памяти.

Thomas Jager 26.06.2019 16:18

В C традиционный способ упаковки битовых полей состоит в том, чтобы упаковать их в целые числа, размер которых совпадает с объявленным целочисленным типом. Итак, в вашем примере они будут упакованы в 8-битные целые числа. Если битовое поле не помещалось, оно переходило к новому целому числу. Я считаю, что требования к упаковке с тех пор были ослаблены, но это все еще распространенная стратегия. Итак, в вашем случае упаковки (1) 2, 2, 4 (2) 5, 3 (3) 4, 2 (4) 7 (5) 4 (6) 5 (7) 5 (9) 6 (9) 6 (10) 5 (11) 4. Итак, вы получили 11 8-битных целых чисел. Если вы хотите улучшить упаковку, переключитесь на более крупный целочисленный тип, например. uint32_t.

Tom Karzes 26.06.2019 16:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
317
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы запустите его через инструмент pahole, вы должны получить объяснение:

struct frameHeader_t {
    uint8_t                    encryption:2;         /*     0: 6  1 */
    uint8_t                    frameVersion:2;       /*     0: 4  1 */
    uint8_t                    probeType:4;          /*     0: 0  1 */
    uint8_t                    dataType:5;           /*     1: 3  1 */
    uint8_t                    measurePeriod:3;      /*     1: 0  1 */
    uint8_t                    remontePerdiod:4;     /*     2: 4  1 */
    uint8_t                    nbrMeasure:2;         /*     2: 2  1 */

    /* XXX 2 bits hole, try to pack */

    uint8_t                    year:7;               /*     3: 1  1 */

    /* XXX 1 bit hole, try to pack */

    uint8_t                    month:4;              /*     4: 4  1 */

    /* XXX 4 bits hole, try to pack */

    uint8_t                    day:5;                /*     5: 3  1 */

    /* XXX 3 bits hole, try to pack */

    uint8_t                    hour:5;               /*     6: 3  1 */

    /* XXX 3 bits hole, try to pack */

    uint8_t                    minute:6;             /*     7: 2  1 */

    /* XXX 2 bits hole, try to pack */

    uint8_t                    second:6;             /*     8: 2  1 */

    /* XXX 2 bits hole, try to pack */

    uint8_t                    randomization:5;      /*     9: 3  1 */

    /* XXX 3 bits hole, try to pack */

    uint8_t                    status:4;             /*    10: 4  1 */

    /* size: 11, cachelines: 1, members: 15 */
    /* bit holes: 8, sum bit holes: 20 bits */
    /* bit_padding: 4 bits */
    /* last cacheline: 11 bytes */
};

Вы используете uint8_t в качестве базового типа, поэтому поля дополняются группами по 8 бит.

Вы должны быть в состоянии полностью исключить заполнение, несколько более переносимо, чем с __attribute((packed)), используя unsigned long long/uint_least64_t (не менее 64 бит) в качестве базового типа битовых полей, но технически базовые типы не-int/non-unsigned-int для битовых полей поддержка не гарантируется, но вы можете использовать unsigned (не менее 16 бит, гарантированных стандартом C) после небольшой реорганизации битовых полей, например, в:

typedef struct frameHeader_t
{
    //16
    unsigned year             : 7;
    unsigned randomization    : 5;
    unsigned month            : 4;

    //16
    unsigned second           : 6;
    unsigned minute           : 6;
    unsigned status           : 4;

    //16
    unsigned hour             : 5;
    unsigned dataType         : 5;
    unsigned probeType        : 4;
    unsigned encryption       : 2;

    //16
    unsigned day              : 5;
    unsigned remontePerdiod   : 4;
    unsigned measurePeriod    : 3;
    unsigned nbrMeasure       : 2;
    unsigned frameVersion     : 2;

}FrameHeader; 
//should be an unpadded 8 bytes as long as `unsigned` is 16,
//32, or 64 bits wide (I don't know of a platform where it isn't)

(Заполнение или его отсутствие не гарантируется, но я никогда не видел, чтобы реализация вставляла его, если в этом не было необходимости.)

проблема с вашим решением заключается в том, что оно не сохраняет порядок полей, что делает его бесполезным, когда полученные данные выбирают свой собственный порядок.

0___________ 26.06.2019 17:27

@P__J__ Я упоминаю, что использование unsigned long long/uint_least64_t должно исправить это без необходимости изменения порядка.

PSkocik 26.06.2019 17:30

@P__J__ Для ввода-вывода я явно манипулирую битами из массива uin8_t, это должно быть наиболее переносимым подходом.

PSkocik 26.06.2019 17:33

чтобы избежать магии упаковки, я обычно использую беззнаковые типы фиксированного размера с тем же размером

typedef struct frameHeader_t
    {
        uint64_t encryption       : 2;
        uint64_t frameVersion     : 2;
        uint64_t probeType        : 4;
        uint64_t dataType         : 5;
        uint64_t measurePeriod    : 3;
        uint64_t remontePerdiod   : 4;
        uint64_t nbrMeasure       : 2;
        uint64_t year             : 7;
        uint64_t month            : 4;
        uint64_t day              : 5;
        uint64_t hour             : 5;
        uint64_t minute           : 6;
        uint64_t second           : 6;
        uint64_t randomization    : 5;
        uint64_t status           : 4;

    }FrameHeader;

https://godbolt.org/z/BX2QsC

Другие вопросы по теме