Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Visual C++ / MFC / WTL > Битовые поля структуры не хотят паковаться


Автор: DrUnkard 14.05.22, 23:07
Решил хитро преобразовывать данные протокола SBUS в байты и обратно. Через объявление структуры и объединение. Но что-то пошло не так... :D
Значения в одном канале для SBUS есть 11-битное число. Отправка же всех данных по USART идёт побайтно.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #pragma pack(push, 1) //
    typedef struct tag_sbus_dat {
        BYTE  StartByte;
        WORD ch0 : 11;
        WORD ch1 : 11;
        WORD ch2 : 11;
        WORD ch3 : 11;
        WORD ch4 : 11;
        WORD ch5 : 11;
        WORD ch6 : 11;
        WORD ch7 : 11;
        WORD ch8 : 11;
        WORD ch9 : 11;
        WORD ch10 : 11;
        WORD ch11 : 11;
        WORD ch12 : 11;
        WORD ch13 : 11;
        WORD ch14 : 11;
        WORD ch15 : 11;
        BYTE  FlagsByte;
        BYTE  EndByte;
    }  SBUS_DAT;
    #pragma pack(pop)
     
    typedef union
    {
        BYTE byte[25];
        SBUS_DAT msg;
    } Sbus_msg;
     
    Sbus_msg Sbus_Data;

Общее количество бит в структуре должно быть 200, что соответствует 25 байт (размер посылки в SBUS).
Задумка такая: формирую значения всех 16 каналов, и выбрасываю их на USART в виде 25 байтов.
И в обратном направлении: Читаю 25 байтов с USART и тут же получаю 11-битные значения всех 16 каналов.

Но на 10 канале число не соответствует реальному. И размер структуры :
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    printf("sizeof data is %d\n", sizeof(Sbus_Data));

Вместо 25 байтов выдает 36.

Где тут засада, не могу понять... :scratch:

Автор: Majestio 15.05.22, 05:34
С битовыми полями не все так просто. По стандарту там многое зависит от реализации. Вот кусочек стандарта "Стандарт-Си++ 2017 (ISO-IEC_14882-2017)" (обрати внимание на выделенное красным):
Цитата

12.2.4 Bit-fields


1. A member-declarator of the form

identifieropt attribute-specifier-seqopt : constant-expression brace-or-equal-initializeropt

specifies a bit-field; its length is set off from the bit-field name by a colon. The optional attribute-specifier-seq appertains to the entity being declared. The bit-field attribute is not part of the type of the class member. The constant-expression shall be an integral constant expression with a value greater than or equal to zero. The value of the integral constant expression may be larger than the number of bits in the object representation (6.9) of the bit-field’s type; in such cases the extra bits are padding bits (6.9). Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementation-defined. Bit-fields are packed into some addressable allocation unit.
[ Note: Bit-fields straddle allocation units on some machines and not on others. Bit-fields are assigned right-to-left on some machines, left-to-right on others. —end note ]

2. A declaration for a bit-field that omits the identifier declares an unnamed bit-field. Unnamed bit-fields are not members and cannot be initialized. [ Note: An unnamed bit-field is useful for padding to conform to externally-imposed layouts. —end note ] As a special case, an unnamed bit-field with a width of zero specifies alignment of the next bit-field at an allocation unit boundary. Only when declaring an unnamed bit-field may the value of the constant-expression be equal to zero.

3. A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (6.9.1). A bool value can successfully be stored in a bit-field of any nonzero size. The address-of operator & shall not be applied to a bit-field, so there are no pointers to bit-fields. A non-const reference shall not be bound to a bit-field (11.6.3). [ Note: If the initializer for a reference of type const T& is an lvalue that refers to a bit-field, the reference is bound to a temporary initialized to hold the value of the bit-field; the reference is not bound to the bit-field directly. See 11.6.3. —end note ]

4. If the value true or false is stored into a bit-field of type bool of any size (including a one bit bit-field), the original bool value and the value of the bit-field shall compare equal. If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type (10.2), the original enumerator value and the value of the bit-field shall compare equal.


А вот кусочек из "Стандарт-Си-2018 (ISO-IEC_ 9899-2018)":

Цитата

6.7.2.1 Structure and union specifiers
...
11.An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
...


Я попробовал в онлайн-компиляторе твой код, предварительно изменив твои типы на типы из стандартной библиотеки - там все как ты хотел:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <cstdio>
    #include <cstdint>
     
    #pragma pack(push, 1) //
    typedef struct tag_sbus_dat {
        uint8_t  StartByte;
        uint16_t ch0 : 11;
        uint16_t ch1 : 11;
        uint16_t ch2 : 11;
        uint16_t ch3 : 11;
        uint16_t ch4 : 11;
        uint16_t ch5 : 11;
        uint16_t ch6 : 11;
        uint16_t ch7 : 11;
        uint16_t ch8 : 11;
        uint16_t ch9 : 11;
        uint16_t ch10 : 11;
        uint16_t ch11 : 11;
        uint16_t ch12 : 11;
        uint16_t ch13 : 11;
        uint16_t ch14 : 11;
        uint16_t ch15 : 11;
        uint8_t  FlagsByte;
        uint8_t  EndByte;
    }  SBUS_DAT;
    #pragma pack(pop)
     
    typedef union
    {
        uint8_t byte[25];
        SBUS_DAT msg;
    } Sbus_msg;
     
    int main()
    {
        Sbus_msg Sbus_Data;
        printf("sizeof data is %ld\n", sizeof(Sbus_Data));
        return 0;
    }


https://onlinegdb.com/Cyn-Rqok-

Автор: DrUnkard 15.05.22, 11:36
Majestio, спасибо.
У меня стандарт Си++ 17
Сходил по твоей ссылке. Там вижу реально размер структуры 25 байт.
Скопировал твой код себе. Запустил - всё равно 35 байтов. >:(
Использую MSVC 2017.

Видимо придётся отказаться от хитрого плана и перекодировать всё вручную. <_<

Автор: Majestio 15.05.22, 12:22
Цитата DrUnkard @
Видимо придётся отказаться от хитрого плана и перекодировать всё вручную.

Думаю, имеет смысл погуглить про особенности именно MSVC 2017.

Автор: DrUnkard 15.05.22, 12:36
Я тут другое поискал. Нашел ручную кодировку / раскодировку SBUS. Буду применять ее и не заморачиваться своими хитростями. :yes-sad:
Кому интересно - тут Функции:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    bool SBUS::read(uint16_t* channels, bool* failsafe, bool* lostFrame)
     
    void SBUS::write(uint16_t* channels)

https://github.com/TheDIYGuy999/SBUS/blob/m...er/src/SBUS.cpp

Спасибо всем добрым людям. :D

Автор: Majestio 15.05.22, 13:02
Цитата Majestio @
Думаю, имеет смысл погуглить про особенности именно MSVC 2017.

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

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)