Есть ли способ контролировать заполнение между членами структуры (включая поле бит) в С++?

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

Например, я хочу определить структуру данных для протокола RTP следующим образом.

class RTPHeader
{
   int version:2; // The first two bits is version.
   int P:1;  // The next bits is an field P.
   int X:1;
   int CC:4;
   int M:1;
   int PT:7;
   int sequenceNumber;
   int64 timestamp;
   .....
};

И используя его таким образом.

RTPHeader header;
memcpy(&header, steamData, sizeof(header));

Но поскольку компилятор С++ будет вставлять дополнения между членами, есть ли способ контролировать это, чтобы никакие дополнения не добавлялись между членами (включая члены битового поля)?

Этот вопрос НЕ является дубликатом Как избавиться от заполнения байтов между элементами данных структуры, потому что в моем примере могут быть битовые поля.

Ответ 1

Пока у вас нет требования, чтобы этот код "работал" на произвольных машинах - например, машины, которые имеют ограничения относительно границ байтов a int (обычно 4 байта), затем используя

 #pragma(pack)

должен работать, а поддерживается в GCC, а также компиляторы Microsoft и совместимые с Microsoft (такие как компилятор Intel).

Но обратите внимание, что неравномерный доступ не поддерживается для всех процессоров, поэтому запуск блока с 16-битным значением, за которым следует 32-разрядный int, возможно, вызовет проблемы.

Я бы также использовал целое число размером для sequencenumber, чтобы обеспечить его 32-бит в компиляторе EVERY, а не внезапно 16 или 64 бита.

Обратите также внимание на то, что в стандарте С++ ничего не говорится о том, что биты хранятся в битовом поле - или если это так, если между ними есть промежутки. Хотя вы можете ожидать, что битподы будут храниться в соответствии с порядком байтов (маленькие конечные машины сначала начинаются с младших бит, а большие конечные машины начинают сначала с битов самого высокого порядка), стандарт ничего не говорит в этом отношении.

Ответ 2

Если вы можете использовать С++ 11, вы можете использовать управление выравниванием, реализованное с помощью оператора alignof.

Если вы не можете использовать компилятор С++ 11, вам могут помочь нестандартные альтернативы; в GCC __attribute__(packed), а с MSVC #pragma pack.

Если ваш выбор является вариантом GCC, атрибут должен быть помещен в конец структуры:

class RTPHeader
{
    int version:2; // The first two bits is version.
    int P:1;  // The next bits is an field P.
    int X:1;
    int CC:4;
    int M:1;
    int PT:7;
    int sequenceNumber;
    int64 timestamp;
    .....
} __attribute__((packed)) ; // attribute here!

Если ваш выбор - это MSVC, прагма должна быть размещена до. struct:

#pragma pack(1) // pragma here!
class RTPHeader
{
    int version:2; // The first two bits is version.
    int P:1;  // The next bits is an field P.
    int X:1;
    int CC:4;
    int M:1;
    int PT:7;
    int sequenceNumber;
    int64 timestamp;
    .....
};

Если ваш код должен быть скомпилирован в обоих случаях, единственным способом (без оператора С++ 11 alignof) является условная компиляция:

#ifdef MSVC
#pragma pack(1)
#endif
class RTPHeader
{
    int version:2; // The first two bits is version.
    int P:1;  // The next bits is an field P.
    int X:1;
    int CC:4;
    int M:1;
    int PT:7;
    int sequenceNumber;
    int64 timestamp;
    .....
#ifdef GCC
}__attribute__((packed));
#else
};
#endif

Ответ 3

Чтобы избежать ввода заполненных байтов, вы можете использовать

#pragma pack(push,n)   // use n = 1 to have 1 Byte resolution

typedef struct {...}MY_STRUCT;

#pragma pack(pop)

Это сработало для меня.

Также рассмотрите варианты компиляции для выравнивания элементов Struct

/Zp1

Но имейте в виду, что это повлияет на весь ваш проект.