Я читал, что порядок битовых полей в структуре зависит от платформы. Что если я использую различные параметры упаковки для конкретного компилятора, будут ли эти данные гарантии храниться в правильном порядке по мере их написания? Например:
struct Message
{
unsigned int version : 3;
unsigned int type : 1;
unsigned int id : 5;
unsigned int data : 6;
} __attribute__ ((__packed__));
На процессоре Intel с компилятором GCC поля располагались в памяти, как показано. Message.version
были первые 3 бита в буфере, а Message.type
затем следовали. Если я найду эквивалентные варианты упаковки структур для различных компиляторов, будет ли это кроссплатформенный?
c++
c
bit-manipulation
endianness
bit
Девальд
источник
источник
Ответы:
Нет, он не будет полностью портативным. Варианты упаковки структур являются расширениями и сами по себе не являются полностью переносимыми. В дополнение к этому, в параграфе 10 C99 §6.7.2.1 говорится: «Порядок распределения битовых полей в блоке (от высокого к низкому или от низкого к высокому) определяется реализацией».
Даже один компилятор может размещать битовое поле по-разному, например, в зависимости от порядка байтов целевой платформы.
источник
packed
исполнение заказа: stackoverflow.com/questions/1756811/... как обеспечить битовую порядок: stackoverflow.com/questions/6728218/gcc-compiler-bit-orderБитовые поля сильно различаются от компилятора к компилятору, извините.
С GCC машины с прямым порядком байтов сначала выкладывают биты с обратным порядком байтов, а машины с обратным порядком байтов сначала размещают биты с обратным порядком.
K&R говорит: «Соседние элементы [битового] поля структур упакованы в зависящие от реализации блоки памяти в направлении, зависящем от реализации. Когда поле, следующее за другим полем, не подходит ... оно может быть разделено между блоками или блок может быть padded. Безымянное поле шириной 0 заставляет это заполнение ... "
Следовательно, если вам нужна машинно-независимая двоичная компоновка, вы должны сделать это самостоятельно.
Это последнее утверждение также применимо к небитовым полям из-за заполнения - однако все компиляторы, похоже, имеют какой-то способ принудительной упаковки байтов в структуру, как я вижу, вы уже обнаружили для GCC.
источник
Следует избегать битовых полей - они не очень переносимы между компиляторами даже для одной и той же платформы. из стандарта C99 6.7.2.1/10 - «Спецификаторы структуры и объединения» (аналогичная формулировка есть в стандарте C90):
Вы не можете гарантировать, будет ли битовое поле "охватывать" границу int или нет, и вы не можете указать, начинается ли битовое поле с нижнего конца int или верхнего конца int (это не зависит от того, является ли процессор прямой или прямой порядок байтов).
Предпочитайте битовые маски. Используйте встроенные строки (или даже макросы) для установки, очистки и тестирования битов.
источник
_BIT_FIELDS_LTOH
и_BIT_FIELDS_HTOL
endianness говорят о байтовых порядках, а не о битовых порядках. В настоящее время битовые порядки фиксированы на 99%. Однако при использовании битовых полей следует учитывать порядок байтов. См. Пример ниже.
#include <stdio.h> typedef struct tagT{ int a:4; int b:4; int c:8; int d:16; }T; int main() { char data[]={0x12,0x34,0x56,0x78}; T *t = (T*)data; printf("a =0x%x\n" ,t->a); printf("b =0x%x\n" ,t->b); printf("c =0x%x\n" ,t->c); printf("d =0x%x\n" ,t->d); return 0; } //- big endian : mips24k-linux-gcc (GCC) 4.2.3 - big endian a =0x1 b =0x2 c =0x34 d =0x5678 1 2 3 4 5 6 7 8 \_/ \_/ \_____/ \_____________/ a b c d // - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2 a =0x2 b =0x1 c =0x34 d =0x7856 7 8 5 6 3 4 1 2 \_____________/ \_____/ \_/ \_/ d c b a
источник
В большинстве случаев, вероятно, но не ставьте на это фарм, потому что, если вы ошибаетесь, вы сильно проиграете.
Если вам действительно, действительно нужно иметь идентичную двоичную информацию, вам нужно будет создать битовые поля с битовыми масками - например, вы используете беззнаковый короткий (16 бит) для сообщения, а затем сделайте такие вещи, как versionMask = 0xE000, чтобы представить три самых верхних бита.
Похожая проблема с выравниванием внутри структур. Например, все процессоры Sparc, PowerPC и 680x0 имеют прямой порядок байтов, и обычно компиляторы Sparc и PowerPC по умолчанию выравнивают элементы структуры по 4-байтовым границам. Однако один компилятор, который я использовал для 680x0, выровнен только по 2-байтовым границам - и не было возможности изменить выравнивание!
Таким образом, для некоторых структур размеры на Sparc и PowerPC идентичны, но меньше на 680x0, а некоторые члены находятся в разных смещениях памяти внутри структуры.
Это была проблема с одним проектом, над которым я работал, потому что серверный процесс, запущенный на Sparc, запрашивал у клиента и обнаруживал, что он был прямым порядком байтов, и предполагал, что он может просто выдавать двоичные структуры в сеть, и клиент может справиться. И это прекрасно работало на клиентах PowerPC и приводило к большим сбоям на клиентах 680x0. Я не писал код, и поиск проблемы занял довольно много времени. Но как только я это сделал, это было легко исправить.
источник
Спасибо @BenVoigt за очень полезный комментарий, начинающийся
Источник Linux делает использование битового поля для согласования с внешней структурой: /usr/include/linux/ip.h имеет этот код для первого байта в дейтаграммах IP
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif
Однако в свете вашего комментария я отказываюсь от попыток заставить это работать для многобайтового битового поля frag_off .
источник
Конечно, лучший ответ - использовать класс, который читает / записывает битовые поля в виде потока. Использование структуры битового поля C просто не гарантируется. Не говоря уже о том, что использование этого в реальном кодировании считается непрофессиональным / ленивым / глупым.
источник