C/C++: Вызовите Порядок Битового поля и Выравнивание

6 ответов

No, it will not be fully-portable. Packing options for structs are extensions, and are themselves not fully portable. In addition to that, C99 §6.7.2.1, paragraph 10 says: "The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined."

Even a single compiler might lay the bit field out differently depending on the endianness of the target platform, for example.

96
ответ дан 24 November 2019 в 09:33
поделиться

Битовые поля сильно различаются от компилятора к компилятору, извините.

С GCC машины с прямым порядком байтов сначала размещают биты с обратным порядком байтов, а машины с обратным порядком байтов сначала размещают биты с обратным порядком.

12114] K&R говорит: «Соседние элементы [битового] ​​поля структур упакованы в зависящие от реализации блоки памяти в направлении, зависящем от реализации. Когда поле, следующее за другим полем, не подходит ... оно может быть разделено между блоками или блоком может быть дополнено. Безымянное поле шириной 0 заставляет это заполнение ... "

Следовательно, если вам нужна машинно-независимая двоичная компоновка, вы должны сделать это самостоятельно.

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

42
ответ дан 24 November 2019 в 09:33
поделиться

endianness are talking about byte orders not bit orders. Nowadays , it is 99% sure that bit orders are fixed. However, when using bitfields, endianness should be taken in count. See the example below.

#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
9
ответ дан 24 November 2019 в 09:33
поделиться

Следует избегать битовых полей - они не очень переносимы между компиляторами даже для одной и той же платформы. из стандарта C99 6.7.2.1/10 - «Спецификаторы структуры и объединения» (аналогичная формулировка есть в стандарте C90):

Реализация может выделить любую адресуемую единицу памяти, достаточно большую для хранения битового поля. Если остается достаточно места, битовое поле, которое следует сразу за другим битовым полем в структуре, должно быть упаковано в соседние биты той же единицы. Если остается недостаточно места, то, помещается ли не подходящее битовое поле в следующий блок или перекрывает соседние блоки, определяется реализацией. Порядок распределения битовых полей внутри блока (от высокого порядка к низшему или от низкого порядка к высокому) определяется реализацией. Выравнивание адресуемого запоминающего устройства не указано.

Вы не можете гарантировать, будет ли битовое поле "охватывать" границу int или нет, и вы не можете указать, начинается ли битовое поле с нижнего конца int или верхнего конца int (это не зависит от того, процессор с прямым порядком байтов или прямым порядком байтов).

Предпочитать битовые маски. Используйте встроенные строки (или даже макросы) для установки, очистки и проверки битов.

33
ответ дан 24 November 2019 в 09:33
поделиться

Most of the time, probably, but don't bet the farm on it, because if you're wrong, you'll lose big.

If you really, really need to have identical binary information, you'll need to create bitfields with bitmasks - e.g. you use an unsigned short (16 bit) for Message, and then make things like versionMask = 0xE000 to represent the three topmost bits.

There's a similar problem with alignment within structs. For instance, Sparc, PowerPC, and 680x0 CPUs are all big-endian, and the common default for Sparc and PowerPC compilers is to align struct members on 4-byte boundaries. However, one compiler I used for 680x0 only aligned on 2-byte boundaries - and there was no option to change the alignment!

So for some structs, the sizes on Sparc and PowerPC are identical, but smaller on 680x0, and some of the members are in different memory offsets within the struct.

This was a problem with one project I worked on, because a server process running on Sparc would query a client and find out it was big-endian, and assume it could just squirt binary structs out on the network and the client could cope. And that worked fine on PowerPC clients, and crashed big-time on 680x0 clients. I didn't write the code, and it took quite a while to find the problem. But it was easy to fix once I did.

6
ответ дан 24 November 2019 в 09:33
поделиться

Of course the best answer is to use a class which reads/writes bit fields as a stream. Using the C bit field structure is just not guaranteed. Not to mention it is considered unprofessional/lazy/stupid to use this in real world coding.

-6
ответ дан 24 November 2019 в 09:33
поделиться