Структура с размером битовых полей - PullRequest
2 голосов
/ 26 июня 2019

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

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;
}

Ответы [ 2 ]

2 голосов
/ 26 июня 2019

Если вы запустите его с помощью инструмента 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 июня 2019

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

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

...