Was ist eine “gepackte” Struktur in C?

Lesezeit: 4 Minuten

Benutzeravatar von PICyourBrain
PICyourBrain

Ich gehe durch einen C-Code, der für den Microchip C30-Compiler geschrieben wurde, und ich sehe oft Strukturen, die wie folgt definiert sind:

typedef struct __attribute__((__packed__)) 
{
    IP_ADDR     MyIPAddr;               // IP address
    IP_ADDR     MyMask;                 // Subnet mask
    IP_ADDR     MyGateway;              // Default Gateway
        // etc...
} APP_CONFIG;

Was bedeutet verpackt?

Benutzeravatar von Juliano
Juliano

Wenn Strukturen definiert werden, darf der Compiler Paddings (Leerzeichen ohne eigentliche Daten) hinzufügen, damit Mitglieder in Adressgrenzen fallen, auf die die CPU leichter zugreifen kann.

Auf einer 32-Bit-CPU sollten 32-Bit-Mitglieder beispielsweise bei Adressen beginnen, die ein Vielfaches von 4 Bytes sind, um effizient auf sie zugreifen zu können (Lesen und Schreiben). Die folgende Strukturdefinition fügt eine 16-Bit-Auffüllung zwischen beiden Mitgliedern hinzu, sodass das zweite Mitglied in eine richtige Adressgrenze fällt:

struct S {
    int16_t member1;
    int32_t member2;
};

Die Speicherstruktur der obigen Struktur in einer 32-Bit-Architektur ist (~ = Polsterung):

+---------+---------+
| m1 |~~~~|   m2    |
+---------+---------+

Beim Packen einer Struktur werden diese Paddings nicht eingefügt. Der Compiler muss mehr Code generieren (der langsamer läuft), um die nicht ausgerichteten Datenelemente zu extrahieren und auch in sie zu schreiben.

Die gleiche Struktur erscheint, wenn sie gepackt ist, im Speicher als etwas wie:

+---------+---------+
| m1 |   m2    |~~~~
+---------+---------+

  • Tangentiale Frage, aber warum sollte man eine gepackte Struktur schaffen, wenn es die Dinge langsamer macht? Soll der Speicherbedarf reduziert werden?

    – verdammt

    12. Dezember 2017 um 6:05 Uhr

  • Ein Anwendungsfall: Sie werden sehr oft zum Definieren und Implementieren von Netzwerkprotokollen, Binärformaten usw. verwendet. Normalerweise möchten Sie Strukturen, die gespeichert oder über das Netzwerk gesendet werden, keine Auffüllungen hinzufügen.

    – jjmontes

    4. April 2018 um 14:25 Uhr

  • In Ihrem Beispiel sind 16 Bit + 32 Bit = 48 Bit = 12 Byte und das ist tatsächlich ein Vielfaches von 4 Byte. Warum sollte ein Compiler dann Padding anwenden?

    – Sprungmann

    23. Oktober 2018 um 11:19 Uhr


  • @Maxitj, 48 Bit sind 6 Bytes, nicht 12.

    – Juliano

    23. Oktober 2018 um 11:50 Uhr

  • @Juliano Dummkopf, war ziemlich müde, als ich diesen Beitrag gelesen habe 🙂

    – Sprungmann

    29. Oktober 2018 um 18:25 Uhr

Benutzeravatar von NPE
NPE

Es weist den Compiler an, keine Auffüllung zwischen Mitgliedern von hinzuzufügen struct.

Siehe zum Beispiel diese Seite.

  • Ich denke, Sie meinen das Gegenteil – verpackt bedeutet, jegliche Polsterung wegzulassen und nicht hinzuzufügen.

    – flolo

    29. März 2011 um 13:26 Uhr

  • Der Link in dieser Antwort ist defekt, gibt es eine Möglichkeit zur Behebung?

    – Kev

    20. November 2011 um 16:41 Uhr

  • Detail: “Keine Auffüllung zwischen Mitgliedern hinzufügen” ist eher wie Hinzufügen Minimum Verpackung. Bestimmte Architekturen können in ausgewählten Fällen immer noch eine gewisse Auffüllung erfordern. (z.B int muss auf gerader Adressgrenze liegen, um Busfehler zu vermeiden.)

    – chux – Wiedereinsetzung von Monica

    12. Juli 2016 um 17:37 Uhr

Benutzeravatar von Babajan
Babajan

Lassen Sie mich das Konzept des Auffüllens in Strukturen und dann von gepackten Strukturen anhand eines Beispiels erläutern.

Und dann lassen Sie uns sehen, warum eine Verpackung erforderlich ist.

Polsterung:

struct eg_struct
{
           unsigned char abc;
           unsigned int  xyz;
}

Wenn die Struktur wie oben auf einer 16-Bit-Architektur deklariert wird, wird die Variable abc würde eine Adresse zugewiesen werden. Die nächste Adresse wird nicht der Variablen zugewiesen xyzstattdessen wird ein zusätzliches Byte hinzugefügt, und dann würde der Variablen die nächste Adresse zugewiesen xyz.

Am Ende sieht die Struktur in etwa so aus:

struct eg_struct
{
           unsigned char abc;
           unsigned char paddedbytes[1];
           unsigned int  xyz;
}

Das Auffüllen macht Adressen von Mitgliedsvariablen für den Mikrocontroller leicht zugänglich. Der Nachteil sind zusätzliche unnötige Bytes, die ins Bild kommen.

Verpackung:

Wenn die gleiche Struktur mit dem Attribut „packed“, wird das zusätzliche Byte nicht nach der Variablen hinzugefügt abc.

Lassen Sie mich ein Beispiel geben, wo eine Verpackung erforderlich ist:

Stellen Sie sich einen Mikrocontroller vor, der mit einem EEPROM verbunden ist, in dem eine Struktur gespeichert wird.

Stellen Sie sich vor, eine Funktion, die in das EEPROM schreibt, würde wie folgt aussehen:

Write_EEPROM(EEPROM address, Ram address, Byte count);

Wenn nun das Packen nicht durchgeführt wird, würden die zusätzlichen aufgefüllten Bytes Platz im EEPROM belegen, was keinen Nutzen hat.

  • @laurenz albe Danke, dass du meine Antwort präsentabler gemacht hast.

    – Babajan

    28. April 2019 um 18:31 Uhr


_attribute__((__packed__)) bedeutet (höchstwahrscheinlich) “keine Auffüllung einfügen, um die Dinge schneller zu machen” und kann auch bedeuten “keine Ausrichtungen einfügen, um die Ausrichtung beizubehalten”.

Eine Sache, die nicht ausdrücklich erwähnt wurde, ist, dass das Packen normalerweise so durchgeführt wird, dass es vordefinierten Feldstrukturen entspricht. Beispielsweise wird auf der unteren Ebene einer Netzwerkschnittstelle eine Reihe von Bytes zwischen vernetzten Maschinen ausgetauscht. Nachdem die Daten empfangen wurden, müssen sie einer übergeordneten Struktur zugeordnet werden, damit die Daten einfach bearbeitet werden können. In diesem Fall ist normalerweise kein Auffüllen erforderlich, sodass die Struktur direkt auf die Bytes abgebildet wird.

Der Netzwerkdatenaustausch beinhaltet auch ein Byte-Endianness-Problem (dh fast alle Netzwerkdaten verwenden das Big-Endian-Format, unabhängig von der Endianness der Quell- und Zielmaschinen).

Außerdem einige Maschinen kann nicht Greifen Sie auf breite Daten in nicht ausgerichteten Adressen zu, beispielsweise können Cortex-M0-Kerne nicht auf 32-Bit-Daten in nicht ausgerichteten 32-Bit-Adressen zugreifen, daher muss in solchen Fällen beim Schreiben von Netzwerkcode darauf geachtet werden.

1397930cookie-checkWas ist eine “gepackte” Struktur in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy