C / C++: wymusza kolejność i wyrównanie pól bitowych

Czytałem, że kolejność pól bitowych w strukturze jest specyficzna dla platformy. A jeśli użyję różnych opcji pakowania specyficznych dla kompilatora, czy to zagwarantuje, że dane będą przechowywane w odpowiedniej kolejności, w jakiej są zapisywane? Na przykład:

struct Message
{
  unsigned int version : 3;
  unsigned int type : 1;
  unsigned int id : 5;
  unsigned int data : 6;
} __attribute__ ((__packed__));

Na procesorze Intela z kompilatorem GCC, pola zostały ułożone w pamięci tak, jak są pokazane. Message.version były pierwszymi 3 bitami w buforze, a Message.type następowały. Jeśli znajdę równoważne opcje pakowania struct dla różnych Kompilatory, czy to będzie wieloplatformowe?

Author: YePhIcK, 2009-09-29

7 answers

Nie, Nie będzie w pełni przenośny. Opcje pakowania dla struktur są rozszerzeniami i same w sobie nie są w pełni przenośne. Oprócz tego, C99 §6.7.2.1, paragraf 10 mówi: "kolejność alokacji pól bitowych w jednostce (od wysokiego do niskiego lub od niskiego do wysokiego) jest zdefiniowana w implementacji."

Nawet pojedynczy kompilator może rozłożyć pole bitowe w różny sposób, na przykład w zależności od endianness docelowej platformy.

 103
Author: Stephen Canon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 01:31:06

Pola bitowe różnią się znacznie od kompilatora do kompilatora, przepraszam.

W GCC, maszyny big endian układają najpierw bity big end, a Maszyny little endian układają najpierw bity little end.

K&r mówi: "przylegające [bit-]elementy pól struktur są pakowane do jednostek pamięci zależnych od implementacji w kierunku zależnym od implementacji. Gdy pole po innym polu nie będzie pasować ... może być podzielona między jednostki lub jednostka może być wyściełana. Nienazwane pole o szerokości 0 wymusza tę wyściółkę..."

Dlatego, jeśli potrzebujesz niezależnego od maszyny układu binarnego, musisz to zrobić sam.

To ostatnie stwierdzenie odnosi się również do nie-bitowych pól z powodu wypełniania -- jednak wszystkie Kompilatory wydają się mieć jakiś sposób wymuszania bajtowego pakowania struktury, jak widzę, że już odkryłeś dla GCC.

 45
Author: Joshua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 01:28:02

Należy unikać Bitfieldów - nie są one zbyt przenośne między kompilatorami, nawet dla tej samej platformy. ze standardu C99 6.7.2.1 / 10 - "structure and union specifiiers" (podobne sformułowanie występuje w standardzie C90):

Implementacja może przydzielić dowolną adresowalną jednostkę pamięci na tyle dużą, aby pomieścić pole bitowe. Jeśli pozostaje wystarczająco dużo miejsca, pole bitowe, które bezpośrednio następuje po innym polu bitowym w strukturze, jest pakowane do sąsiednich bitów tej samej jednostki. Jeśli pozostaje niewystarczająca przestrzeń, niezależnie od tego, czy pole bitowe, które nie pasuje, jest umieszczane w następnej jednostce, czy nakłada się na sąsiednie jednostki, jest definiowane implementacyjnie. Kolejność alokacji pól bitowych w jednostce (od wysokiego do niskiego lub od niskiego do wysokiego) jest zdefiniowana jako implementacja. Wyrównanie adresowalnej jednostki pamięci nie jest określone.

Nie możesz zagwarantować, czy pole bitowe "obejmie" granicę int, czy nie, i nie możesz określić, czy pole bitowe zaczyna się na niskim końcu int lub high end INT (jest to niezależne od tego, czy procesor jest big-endian czy little-endian).

Wolę bitmaski. Użyj linii (lub nawet makr), aby ustawić, wyczyścić i przetestować bity.

 35
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 05:11:53

Endianness mówi o rozkazach bajtowych, a nie rozkazach bitowych. Obecnie jest 99% pewne, że kolejność bitów jest stała. Jednak w przypadku korzystania z bitfields, endianness powinno być brane w count. Zobacz przykład poniżej.

#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
 11
Author: pierrotlefou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-09 07:15:23

Przez większość czasu, prawdopodobnie, ale nie stawiaj farmy na to, bo jeśli się mylisz, stracisz dużo.

Jeśli naprawdę, naprawdę potrzebujesz identycznych informacji binarnych, musisz utworzyć bitfields z bitmask - np. używasz unsigned short (16 bit) dla wiadomości, a następnie tworzyć rzeczy takie jak versionMask = 0xe000, aby reprezentować trzy najwyższe bity.

Jest podobny problem z wyrównaniem w strukturach. Na przykład Procesory Sparc, PowerPC i 680x0 to wszystkie big-endian, a powszechną wartością domyślną dla kompilatorów Sparc i PowerPC jest wyrównywanie elementów struktury na 4-bajtowych granicach. Jednak jeden kompilator używałem dla 680x0 tylko wyrównane na 2-bajtowych granicach - i nie było opcji, aby zmienić wyrównanie!

Tak więc dla niektórych struktur, rozmiary na Sparc i PowerPC są identyczne, ale mniejsze na 680x0, a niektóre z członków są w różnych przesunięciach pamięci wewnątrz struktury.

To był problem z jednym projektem, nad którym pracowałem, ponieważ serwer proces uruchomiony na Sparc odpytywałby klienta i dowiedziałby się, że to big-endian, i założył, że może po prostu wyrzucać binarne struktury w sieci i klient sobie poradzi. I to działało dobrze na klientach PowerPC i rozbiło się na klientach 680x0. Nie napisałem kodu, a znalezienie problemu zajęło sporo czasu. Ale to było łatwe do naprawienia raz zrobiłem.

 6
Author: Bob Murphy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 04:53:13

Thanks @ BenVoigt for your very useful comment starting

Nie, zostały stworzone, aby zachować pamięć.

Źródło Linuksaużywa pola bitowego do dopasowania do zewnętrznej struktury: /usr/include/linux/ip.h posiada ten kod dla pierwszego bajtu datagramu 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

Jednak w świetle Twojego komentarza rezygnuję z prób uruchomienia tego pola dla bitów wielobajtowych frag_off.

 1
Author: Duncan Roe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-27 03:27:32

Oczywiście najlepszą odpowiedzią jest użycie klasy, która odczytuje / zapisuje pola bitowe jako strumień. Użycie struktury pola bitowego C nie jest po prostu gwarantowane. Nie wspominając już o nieprofesjonalnym / leniwym / głupim używaniu tego w kodowaniu w prawdziwym świecie.

 -9
Author: 99999999,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 05:17:04