2016-04-16 6 views
1

Der Versuch, Daten in ein Paket zu packen. Dieses Paket sollte 64 Bits sein. Ich habe dies:Bitfelder und Ausrichtung

typedef union { 
    uint64_t raw; 
    struct { 
    unsigned int magic : 8; 
    unsigned int parity : 1; 
    unsigned int stype : 8; 
    unsigned int sid  : 8; 
    unsigned int mlength : 31; 
    unsigned int message : 8; 
    } spacket; 
} packet_t; 

Aber es scheint, dass die Ausrichtung nicht garantiert ist. Denn wenn ich laufen diese:

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

const char *number_to_binary(uint64_t x) 
{ 
    static char b[65]; 
    b[64] = '\0'; 

    uint64_t z; 
    int w = 0; 
    for (z = 1; w < 64; z <<= 1, ++w) 
    { 
     b[w] = ((x & z) == z) ? '1' : '0'; 
    } 

    return b; 
} 

int main(void) 
{ 
    packet_t ipacket; 
    bzero(&ipacket, sizeof(packet_t)); 
    ipacket.spacket.magic = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.parity = 1; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.stype = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.sid = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.mlength = 2147483647; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.message = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
} 

ich (Big Endian):

1111111100000000000000000000000000000000000000000000000000000000 
1111111110000000000000000000000000000000000000000000000000000000 
1111111111111111100000000000000000000000000000000000000000000000 
1111111111111111111111111000000000000000000000000000000000000000 
1111111111111111111111111000000011111111111111111111111111111110 
1111111111111111111111111000000011111111111111111111111111111110 

Mein .mlength Feld ist irgendwo auf dem rechten Teil verloren, obwohl es direkt neben dem .sid Feld sein sollte.

This page bestätigt es: "Ausrichtung der Zuordnungseinheit, die ein Bitfeld enthält". Aber wenn das der Fall ist, wie verpacken Menschen Daten in Bitfelder, was ist ihr Zweck überhaupt?

24 Bits scheint die maximale Größe zu sein, die das Feld .mlength einnehmen kann, bevor das Feld .message hinausgeschmissen wird.

+2

Im Allgemeinen können Sie sich nicht darauf verlassen, wie die Daten in einem Bitfeld ausgelegt sind.Wenn Sie mehrere Daten in ein Wort packen müssen, müssen Sie das Marshalling manuell durchführen. – fuz

+0

Ihre Struktur ist 32 * 6 Bits ... Sie sollten stattdessen Zeichen verwenden. – xvan

+1

Verlassen Sie sich nicht auf ein bestimmtes Speicherlayout für C-Datenstrukturen. Es gibt einfach zu viele implementierungsdefinierte und compilerspezifische Parameter. Wie @ FUZxxl schrieb, verwenden Sie ordnungsgemäßes Marshalling. Bei einem guten Compiler ist das nicht unbedingt langsamer. – Olaf

Antwort

1

Fast alles über das Layout von Bitfeldern ist im Standard implementierungsdefiniert, wie Sie es aus zahlreichen anderen Fragen zum Thema SO finden. (Unter anderem können Sie sich Questions about bitfields und vor allem Bit field's memory management in C ansehen).

Wenn Sie Ihre Bit-Felder in 64 Bits gepackt werden soll, werden Sie vertrauen müssen, dass Ihr Compiler 64-Bit-Typen für die Felder verwendet werden können, und dann verwenden:

typedef union { 
    uint64_t raw; 
    struct { 
    uint64_t magic : 8; 
    uint64_t parity : 1; 
    uint64_t stype : 8; 
    uint64_t sid  : 8; 
    uint64_t mlength : 31; 
    uint64_t message : 8; 
    } spacket; 
} packet_t; 

Wie Ursprünglich geschrieben, würden Ihre Bitfelder unter einem plausiblen (gemeinsamen) Schema in neue 32-Bit-Wörter aufgeteilt, wenn im aktuellen nicht mehr genügend Platz vorhanden ist. Das heißt, magic, parity, stype und sid würden 25 Bits belegen; es ist nicht genug Platz übrig in einem 32-Bit unsigned int, um weitere 31 Bits zu halten, so dass mlength in dem nächsten unsigned int gespeichert wird, und dort ist nicht genug Platz übrig in diesem Gerät, um message zu speichern, so dass es im dritten gespeichert wird unsigned int Einheit. Das würde Ihnen eine Struktur geben, die 3 * sizeof(unsigned int) oder 12 Bytes besetzt - und die Vereinigung würde wegen der Ausrichtungsanforderungen auf uint64_t 16 Byte belegen.

Beachten Sie, dass der Standard nicht garantiert, dass das, was ich zeige, funktioniert. Unter vielen Compilern wird es wahrscheinlich funktionieren. (Genauer gesagt funktioniert es mit GCC 5.3.0 unter Mac OS X 10.11.4.)

+1

Aber auch dann kann man nicht auf die Reihenfolge der Bitfelder angewiesen sein. Ändern Sie Ihren Compiler, und alles könnte sich ändern. Und genau genommen ist es auch implementationsdefiniert, ob ein Bitfeld in ein neues Strukturelement aufgeteilt werden soll, wenn es nicht in das aktuelle Strukturelement passen kann - es kann Elemente überspannen. –

+0

Wie ich schon sagte, ist im Wesentlichen alles außer ihrer Existenz Implementierung definiert. Die Frage des [Bit-Felds Speicherverwaltung in C] (https://stackoverflow.com/questions/32677235/bit-fields-memory-management-in-c-) hat eine Antwort (von YT), die den Standard ausführlich auf der Thema von Bitfeldern. –

0

Abhängig von Ihrer Architektur und/oder Ihrem Compiler werden Ihre Daten an unterschiedliche Größen angepasst. Aus Ihren Beobachtungen würde ich erraten, dass Sie die Konsequenzen der 32-Bit-Ausrichtung sehen. Wenn Sie sich die Größe Ihrer Vereinigung ansehen und das mehr als 8 Bytes (64 Bits) sind, wurden Daten für die Ausrichtung aufgefüllt.

Mit 32-Bit-Ausrichtung mlength und Nachricht wird nur in der Lage sein, nebeneinander zu bleiben, wenn sie summieren sich auf weniger als oder gleich 32 Bits. Dies ist wahrscheinlich, was Sie mit Ihrem 24-Bit-Limit sehen.

Wenn Sie möchten, dass Ihre Struktur nur 64 Bit mit 32-Bit-Ausrichtung verwendet, müssen Sie sie ein wenig neu anordnen. Die Ein-Bit-Parität sollte neben der 31-Bit-Länge liegen, und Ihre 4-Bit-Variablen sollten zusammen gruppiert sein.