Проблема размера битового поля в Visual Studio C ++ - PullRequest
0 голосов
/ 29 ноября 2018

Я создал и скомпилировал следующий код в GCC, используя упакованный атрибут, и он работал как ожидалось.Но в Visual Studio результаты не совпадают с GCC.

#pragma pack(push, 1)
typedef struct
{
    uint8_t TargetID: 6;
    enum_OPCode OPCode: 3;
    uint8_t CRC7: 7;
} struct_commDataPack_request;
#pragma pack(pop)

Как видите, размер всех элементов должен быть 16 бит = 2 байт, что верно в GCC, но в Visual Studio он возвращает 3 байт!и если я уменьшу его размер до 15 бит, он вернет 2 байта.

Как я могу это исправить?

Ответы [ 3 ]

0 голосов
/ 29 ноября 2018

Я использовал этот код для воспроизведения в VS2017:

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

typedef enum { A,B,C } enum_OPCode;

#pragma pack(push, 1)
typedef struct
{
  uint8_t TargetID : 6;
  uint8_t OPCode : 3;
  uint8_t CRC7 : 7;
} struct_commDataPack_request;
#pragma pack(pop)

int main()
{
  printf("%zd\n", sizeof(struct_commDataPack_request));
}

Размер здесь 3.

Но когда я изменяю

uint8_t OPCode : 3;
uint8_t CRC7 : 7;

на

uint8_t OPCode : 2;
uint8_t CRC7 : 8;

(общий размер остается 16 бит), размер равен 2.

Как было предложено ранее, лучше всего написать собственную сериализацию / десериализацию.

0 голосов
/ 29 ноября 2018

Как указано в MS-документе :

Базовый тип битового поля должен быть целым типом.Если битовое поле переполняет границу объявленного типа (в вашем случае это uint8_t), выделяются новые единицы хранения.

Чтобы решить проблему, используйте объявленный тип сбольшая граница (uint16_t).

Вот код, который я использовал:

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

typedef enum { A, B, C } enum_OPCode;

#pragma pack(push, 1)
typedef struct
{
    uint16_t TargetID : 6;
    uint16_t OPCode : 3;
    uint16_t CRC7 : 7;
} struct_commDataPack_request;
#pragma pack(pop)

int main()
{
    struct_commDataPack_request packet;

    packet.TargetID = 0;
    packet.OPCode = 7;
    packet.CRC7 = 0;

    unsigned char * pData = (unsigned char *)&packet;

    printf("Packet size : %zd\n", sizeof(struct_commDataPack_request));
    for (int i = 0; i < sizeof(packet); i++) {
        printf("byte %d is [%02X]\n", i, pData[i] );
    }
}

Результаты:

Packet size : 2
byte 0 is [C0]
byte 1 is [01]

(0x01C0 в битах: 0000 0001 1100 0000)

0 голосов
/ 29 ноября 2018

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

В VS, когда вы делаете "pack (push, 1)", вы, по сути, устанавливаете заполнение на 8 битов, и, как выпила, в конечном итоге три байта (6 битов из которых заполнены).

Однако заполнение в GCC (если вы сделали это с __attribute __ ((упакованный)) или некоторым псевдонимом для него) может быть полностью отключено,Вот почему вы видите только 2 байта.

Чтобы сохранить его переносимость, почему бы вам просто не написать свою собственную небольшую процедуру сериализации?Как то так:

struct_commDataPack_request s;
short wire = s.TargetID | (s.OPCode<<6) | (s.CRC7 << 9);
...