Speicherausrichtung in C?

4 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Alignment ist die Ausrichtung an der Wortbreite des Systems, diese unterscheidet sich z.B. bei 32-Bit und 64-Bit Systemen - In der Regel wird das Alignment also auf die Architektur optimiert.

Dumpe ich nun eine Structure direkt in eine Datei und lese sie mit anderem Packaging, dann erhalte ich beim Lesen natürlich fehlerhafte Daten. Für persistente Daten sollte man ohnehin auch die Endian-Safeness garantieren.

Richtig krtisch wird das aber bei der Linkage, compiliere ich eine Bibliothek mit einem anderen Packaging als Code der die Bibliothek und deren Datentypen nutzt, dann fliegt mir die ganze Sch... um die Ohren.

Von daher empfiehlt es sich, außer vielleicht in sehr speziellen Fällen, wie bei embedded Systems, die Finger davon zu lassen und den für das System und die Architektur üblichen Standard zu verwenden.

  1. es kann sein, dass die CPU ein bestimmtes alignment braucht...
  2. z. B. könnte es sein, dass eine CPU für einen 32bit-Transfer eine Speicheradresse benötigt, die durch 4 teilbar ist...
  3. wie das nun in deinem Fall ist, weiß ich nicht... kommt auf die CPU an...
  4. würde mich nich wundern, wenn heutige CPUs nur Byte-weise align-te Adressen benötigen...
  5. der 8051 konnte sogar Bits adressieren... LOL
Woher ich das weiß:Studium / Ausbildung – Diplom(U)-Informatik-Studium erfolgreich absolviert...
JOSUE2000 
Fragesteller
 01.11.2018, 22:34

Wieso würde eine CPU eine Speicheradresse benötigen, die durch 4 oder 2 teilbar ist?

0
RIDDICC  01.11.2018, 22:43
@JOSUE2000

also bei geraden Adressen spart man ersichtlich 1 Bit in der Instruktion, weil das letzte Bit ja schon durch den Befehl bekannt ist (nämlich 0)...

bei „durch 4“ sind es 2 bit

bei „durch 8“ sind es 3 bit

1
JOSUE2000 
Fragesteller
 01.11.2018, 23:58
@RIDDICC

Was meinst du mit man erspart sich n Bits?

0
RIDDICC  02.11.2018, 05:44
@JOSUE2000

die Maschinensprache soll ja möglichst kurz sein... aber man will trotzdem was sagen können... da machen einige Designer eben Abstriche bei der Adressierung...

1
IsHaltNeBeta  02.11.2018, 05:54
würde mich nich wundern, wenn heutige CPUs nur Byte-weise align-te Adressen benötigen...

Moderne x86 bzw x64 CPUs benötigen ein entsprechendes Alignment, um effizient arbeiten zu können, bieten aber für viele Instruktionen Alternativen an, die zwar langsamer arbeiten, aber denen dafür Alignment egal ist. :)

Einige dieser Instruktionen kann man beliebig austauschen, und die arbeiten bei einem Alignment von 1 und 4 gleich schnell, bei anderen hingegen wirft die CPU eine Hardware-Ausnahme, wenn man eine Instruktion, die ein größeres Alignment benötigt mit einem unausgerichteten Operanden aufgerufen wird.

1

Alignments sind auch heute noch (unter anderem aus Performancegründen bei rechenintensiven Programmen) wichtig, aber je nach Compiler kann man beliebige Werte von n Bytes angeben, solange n eine Zweierpotenz ist.

Falsch ausglesen wird die Struktur zwar nur, wenn der Compiler die Erweiterung, die Alignments ermöglicht, nicht kennt, aber es bleibt eben eine Erweiterung und die Syntax dafür kann sich je nach System unterscheiden.

Viele C-Programmierer machen leider den Fehler, und schreiben ihre Strukturen wie folgt in eine Datei:

struct Foo {
  int i;
  char c;
};

typedef struct Foo Foo;

/* ... */

Foo foo = { 0, 0 };
FILE *fh = fopen("file.bin", "wb");

/* ... */

fwrite(&foo, sizeof(foo), 1, fh);

Bitte beachte die letzte Zeile des Codeschnipsels: Genau SO sollte man es eben NICHT machen! :)

Das verstößt gegen den Standard, ist undefiniertes und damit nicht portables Verhalten, und reißt Datenlecks bzw. Sicherheitslücken auf.

Der einzige richtige Weg, Strukturen zu schreiben, ist Member für Member:

fwrite(&foo.i, sizeof(foo.i), 1, fh);
fwrite(&foo.c, sizeof(foo.c), 1, fh);

Wer sich für Details interessiert: Falls ich mich nicht irre, hat sowohl Cert-C als auch MISRA-C eine Guideline dazu, und im Buch "Secure Coding in C and C++" gibt es ein Unterkapitel zu dem Thema ... falls ich nicht gerade den Buchtitel mit einem anderen verwechsel.

Auf jeden Fall hat es enorm viele Nachteile und ist genau genommen ein Bug, wenn man Strukturen als Ganzes, und nicht Elementweise schreibt. Und Packing ist sowieso nicht portabel, also am besten entsprechende #pragma oder __attribute__ Direktiven verkneifen, es sei denn, man möchte sich an einen einzigen Compilerhersteller binden. :)

Woher ich das weiß:Berufserfahrung
IsHaltNeBeta  02.11.2018, 05:45

Nachtrag: Um deine eigentliche Frage zu beantworten ...

Alignment, Padding und Packing sind unterschiedliche Dinge, die jedoch gerade bei Arrays einen Einfluss auf einander haben.

Mit dem Elementweisen schreiben und lesen vom obigen Beispiel musst du dir aber um beides keine Gedanken machen, und brauchst dich nicht mit Compilererweiterungen rumschlagen.

1
KarlRanseierIII  02.11.2018, 05:58

Eine struct als ganzes zu 'dumpen' ist nicht per se fehlerhaft. Es ist nur nicht portabel und aus bekannten Gründen unsicher. ABER:

Man kreiere eine shared mmap() mit filebacking, dort lege man geteilte Datenstruckturen für IPC ab. Würde ich structures komponentenweise serialisiert in das mapping legen, führte ich das Prinzip weitgehend ad absurdum. Zumindest aber müßte ich die Serialisierung extrem teuer bezahlen.

Das ist nebenbei auch ein Beispiel (neben der Linkage) wo unterschiedliches Packing mir sehr schnell auf die Füße fällt.

Letztlich muß ich hier Unsicherheit gegen die Vorteile abwiegen, aber in dem Bereich habe ich ohnehin schon lange die Portabilität verlassen.

---

Das nur als ergänzender Denkanstoß.

2