Ich habe mich gefragt, ob mir jemand erklären könnte, was die #pragma pack
Präprozessor-Anweisung bewirkt und was noch wichtiger ist, warum man sie verwenden möchte.
Ich habe mir die MSDN-Seite angesehen , die einige Einblicke bot, aber ich hatte gehofft, mehr von erfahrenen Leuten zu hören. Ich habe es schon einmal im Code gesehen, obwohl ich anscheinend nicht mehr finde, wo.
c
c-preprocessor
pragma-pack
Cenoc
quelle
quelle
#pragma
Anweisungen sind sie implementierungsdefiniert.A mod s = 0
wobei A die Adresse und s die Größe des Datentyps ist; Dies prüft, ob Daten nicht falsch ausgerichtet sind.Antworten:
#pragma pack
Weist den Compiler an, Strukturelemente mit einer bestimmten Ausrichtung zu packen. Die meisten Compiler fügen beim Deklarieren einer Struktur einen Abstand zwischen den Elementen ein, um sicherzustellen, dass sie an den entsprechenden Adressen im Speicher ausgerichtet sind (normalerweise ein Vielfaches der Größe des Typs). Dies vermeidet die Leistungseinbußen (oder Fehler) bei einigen Architekturen, die mit dem Zugriff auf Variablen verbunden sind, die nicht richtig ausgerichtet sind. Beispiel: Geben Sie 4-Byte-Ganzzahlen und die folgende Struktur an:Der Compiler kann die Struktur wie folgt im Speicher anordnen:
und
sizeof(Test)
wäre 4 × 3 = 12, obwohl es nur 6 Datenbytes enthält. Der häufigste Anwendungsfall für#pragma
(meines Wissens) ist die Arbeit mit Hardwaregeräten, bei denen Sie sicherstellen müssen, dass der Compiler keine Auffüllungen in die Daten einfügt und jedes Mitglied dem vorherigen folgt. Mit#pragma pack(1)
würde die obige Struktur wie folgt aufgebaut sein:Und
sizeof(Test)
wäre 1 × 6 = 6.Mit
#pragma pack(2)
würde die obige Struktur wie folgt aufgebaut sein:Und
sizeof(Test)
wäre 2 × 4 = 8.Die Reihenfolge der Variablen in struct ist ebenfalls wichtig. Mit Variablen, die wie folgt geordnet sind:
und mit
#pragma pack(2)
würde die Struktur so angelegt sein:und
sizeOf(Test)
wäre 3 × 2 = 6.quelle
#pragma
wird verwendet, um nicht portierbare Nachrichten (wie nur in diesem Compiler) an den Compiler zu senden. Dinge wie das Deaktivieren bestimmter Warnungen und Verpackungsstrukturen sind häufige Gründe. Das Deaktivieren bestimmter Warnungen ist besonders nützlich, wenn Sie mit den Warnungen kompilieren, wenn das Fehlerflag aktiviert ist.#pragma pack
wird speziell verwendet, um anzuzeigen, dass die Elemente der zu packenden Struktur nicht ausgerichtet sein sollten. Dies ist nützlich, wenn Sie eine speicherabgebildete Schnittstelle zu einer Hardware haben und genau steuern müssen, wohin die verschiedenen Strukturelemente zeigen. Dies ist insbesondere keine gute Geschwindigkeitsoptimierung, da die meisten Maschinen viel schneller mit ausgerichteten Daten umgehen können.quelle
Es teilt dem Compiler die Grenze mit, an der Objekte in einer Struktur ausgerichtet werden sollen. Zum Beispiel, wenn ich so etwas habe wie:
Bei einer typischen 32-Bit - Maschine, würden Sie normalerweise „wollen“ hat drei Bytes padding zwischen
a
undb
so , dassb
bei einer 4-Byte - Grenze landen wird seine Zugriffsgeschwindigkeit zu maximieren (und das ist , was typischerweise durch Standard passieren wird).Wenn Sie jedoch mit einer extern definierten Struktur übereinstimmen müssen, möchten Sie sicherstellen, dass der Compiler Ihre Struktur genau gemäß dieser externen Definition auslegt. In diesem Fall können Sie den Compiler geben ein ,
#pragma pack(1)
ihm zu sagen , nicht jede Polsterung zwischen den Mitgliedern einzusetzen - wenn die Definition der Struktur Polsterung zwischen den Mitgliedern enthält, können Sie es explizit einfügen (zB typischerweise mit Mitgliedern genanntunusedN
oderignoreN
, oder etwas auf , dass Auftrag).quelle
b
an einer 4-Byte-Grenze bedeutet, dass der Prozessor sie laden kann, indem er eine einzelne 4-Byte-Last ausgibt . Obwohl dies etwas vom Prozessor abhängt, besteht eine gute Chance, dass der Prozessor bei einer ungeraden Grenze zwei separate Ladeanweisungen ausgibt und diese Teile dann mit einem Shifter zusammenfügt. Eine typische Strafe liegt in der Größenordnung von 3x langsamerer Ladung dieses Gegenstands.Datenelemente (z. B. Mitglieder von Klassen und Strukturen) werden normalerweise an WORD- oder DWORD-Grenzen für Prozessoren der aktuellen Generation ausgerichtet, um die Zugriffszeiten zu verbessern. Das Abrufen eines DWORD an einer Adresse, die nicht durch 4 teilbar ist, erfordert mindestens einen zusätzlichen CPU-Zyklus auf einem 32-Bit-Prozessor. Wenn Sie also z. B. drei Zeichenmitglieder haben
char a, b, c;
, benötigen diese tatsächlich 6 oder 12 Byte Speicherplatz.#pragma
Mit dieser Option können Sie diese überschreiben, um eine effizientere Speicherplatznutzung auf Kosten der Zugriffsgeschwindigkeit oder eine Konsistenz der gespeicherten Daten zwischen verschiedenen Compilerzielen zu erzielen. Ich hatte viel Spaß beim Übergang von 16-Bit- zu 32-Bit-Code. Ich gehe davon aus, dass die Portierung auf 64-Bit-Code bei einigen Codes die gleichen Kopfschmerzen verursacht.quelle
char a,b,c;
werden normalerweise entweder 3 oder 4 Bytes Speicher benötigt (mindestens auf x86) - das liegt daran, dass ihre Ausrichtungsanforderung 1 Byte beträgt. Wenn nicht, wie würden Sie dann damit umgehenchar str[] = "foo";
? Der Zugriff auf achar
ist immer eine einfache Fetch-Shift-Maske, während der Zugriff auf eineint
Fetch-Fetch-Merge oder nur Fetch erfolgen kann, je nachdem, ob sie ausgerichtet ist oder nicht.int
hat (auf x86) eine 32-Bit-Ausrichtung (4 Byte), da Sie sonstint
eineDWORD
halbe in der einen und eine halbe in der anderen erhalten würden, und das würde zwei Suchvorgänge erfordern.Der Compiler kann Elemente in Strukturen ausrichten, um auf der bestimmten Plattform maximale Leistung zu erzielen.
#pragma pack
Mit der Direktive können Sie diese Ausrichtung steuern. Normalerweise sollten Sie es standardmäßig belassen, um eine optimale Leistung zu erzielen. Wenn Sie eine Struktur an den Remote-Computer übergeben müssen, werden Sie im Allgemeinen verwendet#pragma pack 1
, um unerwünschte Ausrichtungen auszuschließen.quelle
Ein Compiler kann Strukturelemente aus Gründen der Leistung auf einer bestimmten Architektur an bestimmten Bytegrenzen platzieren. Dies kann zu unbenutzten Polstern zwischen den Mitgliedern führen. Die Strukturpackung zwingt die Elemente dazu, zusammenhängend zu sein.
Dies kann beispielsweise wichtig sein, wenn Sie eine Struktur benötigen, die einer bestimmten Datei oder einem bestimmten Kommunikationsformat entspricht, in dem sich die Daten, die Sie benötigen, an bestimmten Positionen innerhalb einer Sequenz befinden müssen. Eine solche Verwendung befasst sich jedoch nicht mit Endianitätsproblemen. Obwohl sie verwendet wird, ist sie möglicherweise nicht portabel.
Es kann auch sein, dass die interne Registerstruktur eines E / A-Geräts wie beispielsweise eines UART- oder USB-Controllers genau überlagert wird, damit der Registerzugriff über eine Struktur und nicht über direkte Adressen erfolgt.
quelle
Sie möchten dies wahrscheinlich nur verwenden, wenn Sie auf einer Hardware (z. B. einem Gerät mit Speicherzuordnung) codieren, für die strenge Anforderungen an die Reihenfolge und Ausrichtung der Register gestellt wurden.
Dies scheint jedoch ein ziemlich stumpfes Werkzeug zu sein, um dieses Ziel zu erreichen. Ein besserer Ansatz wäre, einen Minitreiber in Assembler zu codieren und ihm eine C-Aufrufschnittstelle zu geben, anstatt mit diesem Pragma herumzufummeln.
quelle
Ich habe es schon einmal im Code verwendet, allerdings nur als Schnittstelle zu Legacy-Code. Dies war eine Mac OS X Cocoa-Anwendung, die Voreinstellungsdateien aus einer früheren Carbon-Version laden musste (die selbst abwärtskompatibel mit der ursprünglichen M68k System 6.5-Version war ... Sie haben die Idee). Die Voreinstellungsdateien in der Originalversion waren ein binärer
#pragma pack(1)
Speicherauszug einer Konfigurationsstruktur, mit dem vermieden wurde, zusätzlichen Speicherplatz zu beanspruchen und Junk zu sparen (dh die Füllbytes, die sich sonst in der Struktur befinden würden).Die ursprünglichen Autoren des Codes hatten auch
#pragma pack(1)
Strukturen gespeichert, die als Nachrichten in der Kommunikation zwischen Prozessen verwendet wurden. Ich denke, der Grund hier war, die Möglichkeit unbekannter oder geänderter Auffüllgrößen zu vermeiden, da der Code manchmal einen bestimmten Teil der Nachrichtenstruktur betrachtete, indem er von Anfang an eine Anzahl von Bytes zählte (ewww).quelle
Ich habe gesehen, dass Leute es verwenden, um sicherzustellen, dass eine Struktur eine ganze Cache-Zeile benötigt, um eine falsche Freigabe in einem Multithread-Kontext zu verhindern. Wenn Sie eine große Anzahl von Objekten haben, die standardmäßig lose gepackt werden, kann dies Speicherplatz sparen und die Cache-Leistung verbessern, um sie enger zu packen, obwohl ein nicht ausgerichteter Speicherzugriff normalerweise die Dinge verlangsamt, sodass möglicherweise ein Nachteil auftritt.
quelle
Beachten Sie, dass es andere Möglichkeiten gibt, Datenkonsistenz zu erreichen, die #pragma pack bietet (zum Beispiel verwenden einige Leute #pragma pack (1) für Strukturen, die über das Netzwerk gesendet werden sollen). Siehe zum Beispiel den folgenden Code und seine nachfolgende Ausgabe:
Die Ausgabe ist wie folgt: sizeof (Struktur a): 15, sizeof (Struktur b): 24 sizeof (twoa): 30, sizeof (twob): 48
Beachten Sie, wie die Größe der Struktur a ist genau das, was die Byte - Zählung, aber struct b hat padding hinzugefügt (siehe diese Einzelheiten über die Polsterung). Auf diese Weise können Sie im Gegensatz zum # Pragma-Paket steuern, ob das "Drahtformat" in die entsprechenden Typen konvertiert wird. Zum Beispiel "char two [2]" in ein "short int" und so weiter.
quelle
sizeof
gibt ein zurück,size_t
das mit ausgedruckt werden muss%zu
. Die Verwendung des falschen Formatbezeichners ruft undefiniertes Verhalten hervorWarum will man es benutzen?
Den Speicher der Struktur reduzieren
Warum sollte man es nicht benutzen?
quelle