webentwicklung-frage-antwort-db.com.de

#pragma pack effekt

Ich frage mich, ob mir jemand erklären könnte, was die #pragma pack-Präprozessor-Anweisung tut, und was noch wichtiger ist, warum man sie verwenden möchte.

Ich habe die MSDN-Seite durchgelesen, die einige Einblicke bot, aber ich hoffte, mehr von erfahrenen Leuten zu hören. Ich habe es schon einmal im Code gesehen, obwohl ich scheinbar nicht mehr finde, wo ich bin.

188
Cenoc

#pragma pack weist den Compiler an, Strukturelemente mit einer bestimmten Ausrichtung zu packen. Wenn Sie eine Struktur deklarieren, fügen die meisten Compiler einen Abstand zwischen den Elementen ein, um sicherzustellen, dass sie auf die entsprechenden Adressen im Speicher (normalerweise ein Vielfaches der Größe des Typs) ausgerichtet sind. Dies vermeidet die Leistungseinbußen (oder einen eindeutigen Fehler) bei einigen Architekturen, die mit dem Zugriff auf Variablen verbunden sind, die nicht ordnungsgemäß ausgerichtet sind. Angenommen, 4-Byte-Ganzzahlen und die folgende Struktur:

struct Test
{
   char AA;
   int BB;
   char CC;
};

Der Compiler könnte die Struktur so in den Speicher legen:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

und sizeof(Test) wäre 4 × 3 = 12, obwohl es nur 6 Byte Daten enthält. Der häufigste Anwendungsfall für #pragma (meines Wissens) ist der Fall, wenn Sie mit Hardwaregeräten arbeiten, bei denen Sie sicherstellen müssen, dass der Compiler keine Auffüllung in die Daten einfügt und jedes Member dem vorherigen folgt. Mit #pragma pack(1) würde die Struktur wie folgt aussehen:

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

Und sizeof(Test) wäre 1 × 6 = 6.

Mit #pragma pack(2) würde die Struktur wie folgt aussehen:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

Und sizeof(Test) wäre 2 × 4 = 8.

351
Nick Meyer

#pragma wird verwendet, um nicht portierbare (nur in diesem Compiler) Nachrichten an den Compiler zu senden. Dinge wie das Deaktivieren bestimmter Warnungen und Packstrukturen sind häufige Gründe. Das Deaktivieren bestimmter Warnungen ist besonders hilfreich, wenn Sie die Warnungen als aktiviertes Fehlerflag verwenden.

#pragma pack wird speziell verwendet, um anzuzeigen, dass die Struktur der zu verpackenden Struktur nicht mit den Mitgliedern übereinstimmen sollte. Dies ist nützlich, wenn Sie über eine speicherzugeordnete Schnittstelle zu einer Hardware verfügen und in der Lage sein müssen, genau zu steuern, wo die verschiedenen Strukturelemente zeigen. Es ist vor allem keine gute Geschwindigkeitsoptimierung, da die meisten Maschinen mit ausgerichteten Daten viel schneller umgehen.

21
nmichaels

Sie teilt dem Compiler die Grenze mit, an der Objekte in einer Struktur ausgerichtet werden sollen. Zum Beispiel, wenn ich etwas habe:

struct foo { 
    char a;
    int b;
};

Bei einer typischen 32-Bit-Maschine "möchten" Sie normalerweise 3 Byte langes Auffüllen zwischen a und b haben, damit b an einer 4-Byte-Grenze landet, um die Zugriffsgeschwindigkeit zu maximieren (was normalerweise standardmäßig geschieht.) ). 

Wenn Sie jedoch eine extern definierte Struktur zuordnen müssen, möchten Sie sicherstellen, dass der Compiler Ihre Struktur genau nach dieser externen Definition auslegt. In diesem Fall können Sie dem Compiler eine #pragma pack(1) geben, um es anzuweisen nicht , um eine Auffüllung zwischen Elementen einzufügen. Wenn die Definition der Struktur eine Auffüllung zwischen Elementen enthält, fügen Sie sie explizit ein (z. B. normalerweise mit Mitgliedern mit dem Namen unusedN oder ignoreN oder etwas in dieser Reihenfolge).

15
Jerry Coffin

Datenelemente (z. B. Mitglieder von Klassen und Strukturen) werden normalerweise an Word- oder DWORD-Grenzen für aktuelle Generierungsprozessoren ausgerichtet, um 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 z. Bei drei Zeichen char a, b, c; benötigen sie normalerweise 6 oder 12 Byte Speicherplatz. 

Mit #pragma können Sie dies überschreiben, um eine effizientere Speicherplatznutzung auf Kosten der Zugriffsgeschwindigkeit oder die Konsistenz der gespeicherten Daten zwischen verschiedenen Compilerzielen zu erreichen. Ich hatte viel Spaß beim Umschalten von 16-Bit- auf 32-Bit-Code. Ich gehe davon aus, dass die Portierung auf 64-Bit-Code die gleiche Art von Kopfschmerzen für Code verursacht. 

7
Pontus Gagge

Ein Compiler kann kann Strukturmitglieder aus Performancegründen in einer bestimmten Architektur auf bestimmte Byte-Grenzen setzen. Dies kann dazu führen, dass zwischen den Mitgliedern nicht verwendete Auffüllungen verbleiben. Die Strukturpackung zwingt die Mitglieder dazu, zusammenhängend zu sein. 

Dies kann beispielsweise wichtig sein, wenn Sie eine Struktur benötigen, die mit einer bestimmten Datei oder einem bestimmten Kommunikationsformat übereinstimmt, in dem die Daten, an denen sich die Daten befinden, an bestimmten Positionen innerhalb einer Sequenz liegen. Diese Verwendung befasst sich jedoch nicht mit Endianitätsproblemen, so dass sie zwar nicht verwendet werden kann, jedoch möglicherweise nicht portierbar ist.

Es kann auch die interne Registerstruktur einiger E/A-Geräte, z. B. eines UART oder eines USB-Controllers, genau überlagert werden, damit der Registerzugriff über eine Struktur statt über direkte Adressen erfolgt.

2
Clifford

Der Compiler könnte Mitglieder in Strukturen ausrichten, um auf einer bestimmten Plattform die maximale Leistung zu erzielen. Mit der Direktive #pragma pack können Sie diese Ausrichtung steuern. Normalerweise sollten Sie es für eine optimale Leistung standardmäßig belassen. Wenn Sie eine Struktur an den Remote-Computer übergeben müssen, verwenden Sie #pragma pack 1, um unerwünschte Ausrichtung auszuschließen.

Sie möchten dies wahrscheinlich nur verwenden, wenn Sie auf eine Hardware codieren (z. B. ein Gerät mit Speicherzuordnung), das strikte Anforderungen an die Registerreihenfolge und Ausrichtung hat.

Dies scheint jedoch ein ziemlich stumpfes Werkzeug zu sein, um dieses Ziel zu erreichen. Ein besserer Ansatz wäre, einen Minitreiber im Assembler zu codieren und ihm eine C-aufrufende Schnittstelle zu geben, anstatt mit diesem Pragma herumzufummeln.

1
msw

Ich habe es schon früher im Code verwendet, allerdings nur, um sich mit altem Code zu verbinden. 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 Version M68k System 6.5 war ... Sie haben die Idee). Die Voreinstellungsdateien in der Originalversion waren ein binärer Speicherauszug einer Konfigurationsstruktur, der #pragma pack(1) verwendete, um zu vermeiden, dass zusätzlicher Speicherplatz benötigt wird und Speicherplatz gespart wird (d. H. Die Auffüllbytes, die sich sonst in der Struktur befinden würden).

Die ursprünglichen Autoren des Codes hatten #pragma pack(1) verwendet, um Strukturen zu speichern, die als Nachrichten in der Kommunikation zwischen Prozessen verwendet wurden. Ich denke, der Grund hier war, die Möglichkeit unbekannter oder geänderter Füllungsgrößen zu vermeiden, da der Code manchmal einen bestimmten Teil der Nachrichtenstruktur betrachtet hat, indem eine Anzahl von Bytes von Anfang an gezählt wurde (ewww).

1
user23743

Ich habe gesehen, dass die Benutzer es verwenden, um sicherzustellen, dass eine Struktur eine gesamte Cache-Zeile benötigt, um die falsche Weitergabe in einem Multithread-Kontext zu verhindern. Wenn Sie eine große Anzahl von Objekten haben, die standardmäßig lose gepackt werden, kann dies Speicher sparen und die Cache-Leistung verbessern, um sie enger zu packen. Allerdings wird der Zugriff durch unausgerichteten Speicher in der Regel langsamer, sodass ein Nachteil entsteht.

1
stonemetal

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:

#include <stdio.h>

struct a {
    char one;
    char two[2];
    char eight[8];
    char four[4];
};

struct b { 
    char one;
    short two;
    long int eight;
    int four;
};

int main(int argc, char** argv) {
    struct a twoa[2] = {}; 
    struct b twob[2] = {}; 
    printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b));
    printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob));
}

Die Ausgabe ist wie folgt: Sizeof (struct a): 15, sizeof (struct b): 24 Sizeof (zweia): 30, sizeof (twob): 48

Beachten Sie, dass die Größe von struct a genau der Byteanzahl entspricht, aber bei Struktur b wurde die Auffüllung hinzugefügt (siehe this für Details zur Auffüllung). Im Gegensatz zum #pragma-Paket können Sie das "Drahtformat" in die entsprechenden Typen konvertieren. Zum Beispiel "char two [2]" in ein "short int" und so weiter.

0
wangchow