einen schönen guten abend,
ich habe eine Struktur mit zwei uint8 elementen
1
typedefstruct
2
{
3
uint8_ta;
4
uint8_tb;
5
}testStruct_ts;
6
7
intmain(void)
8
{
9
statictestStruct_tstestStruct_s;
10
testStruct_s.a=2;
11
testStruct_s.b=0;
12
}
wenn ich mir jetzt den Speicher mit GDB ansehe, sehe ich dass die
Struktur an der Adresse 0x762C70 liegt. Wenn ich mir jetzt die Werte
ansehe
1
x/8ub0x762c70
bekomme ich die Ausgabe
1
0x762c70: 2 240 173 186 0 50 186 186
man sieht das dort einige bytes drinn sind, die müll sind (bzw. wo das
drinn steht was da zufällig war).
Aber heißt das, dass das Programm für die Elemente in der Struktur immer
32Bit speicher vorbehält?
Also wäre es letztendlich egal, ob ich uint8_t oder uint32_t benutze?
Verlass dich nicht darauf, dass der Compiler sinnlosen Code exakt so
erzeugt, wie du es erwartest. Es könnte nämlich sein, dass er die
Sinnlosigkeit erkennt und dich verarscht.
Testcode sollte man so erzeugen, dass der Compiler den Code so erzeugen
muss,vwue du es haben willst. Hier: Nimm keine lokale Struct, sondern
eine globale.
Marcel schrieb:> wenn ich mir jetzt den Speicher mit GDB ansehe, sehe ich dass die> Struktur an der Adresse 0x762C70 liegt. Wenn ich mir jetzt die Werte> ansehe
Unoptimiert übersetzt wird die Struct zu Laufzeit initialisiert, nicht
zur Übersetzungszeit. Hast du darauf geachtet, dass dieser
Initialisierungscode gelaufen ist, bevor du den Inhalt angesehen hast?
Optimiert übersetzt verschwindet die Struct völlig, weil nicht
verwendet.
(prx) A. K. schrieb:> . Der Compiler wird die beiden Bytes direkt hintereinander legen, auch> ohne dass es erzwungen werden muss.
Warum? Also warum darf er z.b. nicht auf 4 Byte alignen?
A. S. schrieb:> Warum? Also warum darf er z.b. nicht auf 4 Byte alignen?
Darf er, wenn die Implementierung das für sinnvoll hält. Beispielsweise
wenn die Maschine überhaupt keine 8-Bit Datentypen kennt. Da dann
allerdings so gut wie kein existierender Code anderer Maschinen
übernommen werden kann, wird das kein Compiler bei den üblichen
Prozessoren tun.
Entsprechendes gibt das ABI einer Plattform vor. Und da ist die Regel
verbreitet, dass Struct-Elemente entsprechend ihrer
Alignment-Erfordernisse angeordnet werden, wenn im Code nichts anderes
spezifiziert ist. Ein x86 Compiler für Linux wird sinnvollerweise das
ABI dieses Linux respektieren.
Obacht: Es geht hier um eine Struct. Die Anordnung aufeinanderfolgender
Variablen ohne Kopplung durch Array oder Struct ist eine völlig andere
Baustelle.
(prx) A. K. schrieb:> Verlass dich nicht darauf, dass der Compiler sinnlosen Code exakt so> erzeugt, wie du es erwartest. Es könnte nämlich sein, dass er die> Sinnlosigkeit erkennt und dich verarscht.
Ich würde eher sagen "…sich nicht verarschen lässt."
(prx) A. K. schrieb:> A. S. schrieb:>> Warum? Also warum darf er z.b. nicht auf 4 Byte alignen?>> Darf er, wenn die Implementierung das für sinnvoll hält.
Wäre an der Stelle aber ziemlich unnötig, denn der Compiler muss bei
uint8_t so oder so mit einem Alignment von 1 klar kommen, da spätestens
bei einem Array alle Werte direkt hintereinander hängen müssen.
> Beispielsweise wenn die Maschine überhaupt keine 8-Bit Datentypen kennt.
Dann ist es am sinnvollsten, dem Programmierer auch gar nicht erst einen
anzubieten.
Rolf M. schrieb:> denn der Compiler muss bei uint8_t so oder so mit einem Alignment von 1> klar kommen, da spätestens bei einem Array alle Werte direkt> hintereinander hängen müssen.
Das ist richtig. Trotzdem kann es sein, dass der Prozessor auf das erste
von 4 Bytes schneller zugreift und er es vorzieht, wenn er darf. Hat der
Compiler bei unserem melps' (RISC) auch so gemacht, wenn Geschwindigkeit
zählt.
A. S. schrieb:> Das ist richtig. Trotzdem kann es sein, dass der Prozessor auf das erste> von 4 Bytes schneller zugreift und er es vorzieht, wenn er darf.
Nicht nur schneller, manche CPU kann von Haus aus überhaupt keinen
"unaligned data access" alias zugriff auf ungerade Adressen. Die Cortex
M0 (nicht zu verwechseln mit den M0+) sind solche Gurken.
-
https://www.iar.com/knowledge/support/technical-notes/compiler/accessing-unaligned-data/
Wenn man es doch versucht in c/c++ zu erzwingen muss der Compiler schon
einiges an Schiebereien und Maskierungen einbauen um ein "ungerades"
Byte in einem Register an passender Stelle(LSB) zur Verarbeitung
anzubieten zu können. Wenn immer nur 2/4/8 Byte von entsprechend
ausgerichteten Adressen geladen werden können kostet das dann halt
Geschwindigkeit...
"Unaligned Memory Access" ist so ein unendliches Thema wo man den
compiler am besten einfach machen lässt und nur eingreift wenn es
unbedingt sein muss:-)
http://bompbook.org/ch09.htmlhttps://www.kernel.org/doc/Documentation/unaligned-memory-access.txt
Die meisten Compiler erweitern die unterliegenden Struktur-Komponenten
typischerweise auf die größte der enthaltenen Komponenten:
1
struct{
2
uint8_tb;
3
uint32_ta;
4
uint8_tc;
5
}
So ist das o.g. Aggregat oft 12 Byte groß. Erweitert man die Komponente
a auf uint64_t, so wächst die Größe auf 24 Byte.
Liegen jedoch kleinere Komponenten "nebeneinander", so werden die wieder
typischerweise zusammen gefasst:
1
typedefstruct{
2
uint64_ta;
3
uint16_tb;
4
uint8_tc;
5
}
Das obige Aggregat benötigt dann wieder oft nur 16 Byte.
Wilhelm M. schrieb:> Die meisten Compiler erweitern die unterliegenden Struktur-Komponenten> typischerweise auf die größte der enthaltenen Komponenten:
[...]
> Liegen jedoch kleinere Komponenten "nebeneinander", so werden die wieder> typischerweise zusammen gefasst:
Das ist zwar richtig, aber eine etwas eigenartige Sichtweise.
Letztendlich wird die Struktur eben so erweitert, dass auch bei einem
Array daraus jedes Struktur-Element von jedem Array-Element das
korrekte/optimale Alignment hat. Meist ist das Alignment eines
primitiven Typs ein ganzzahliges Vielfaches von dessen Größe.
Wilhelm M. schrieb:> struct {> uint8_t b;> uint32_t a;> uint8_t c;> }
Wenn ich mir ein Array x[] aus diesen Strukturen vorstelle, dann müssen
nach b 3 Füllbytes eingefügt werden, damit x[0].a an einem Vielfachen
von 4 ausgerichtet ist. Wäre jetzt nach c die Struktur direkt zu Ende,
dann wäre das Alignment von x[1].a nicht passend, da es an Position 13
landen würde.
Deshalb muss die struct nach c ebenfalls um 3 zusätzliche Füllbytes
erweitert werden.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Die meisten Compiler erweitern die unterliegenden Struktur-Komponenten>> typischerweise auf die größte der enthaltenen Komponenten:> [...]>> Liegen jedoch kleinere Komponenten "nebeneinander", so werden die wieder>> typischerweise zusammen gefasst:>> Das ist zwar richtig, aber eine etwas eigenartige Sichtweise.
Das ist nicht eigenartig, sondern genau das was meistens passiert.
> Letztendlich wird die Struktur eben so erweitert, dass auch bei einem> Array daraus jedes Struktur-Element von jedem Array-Element das> korrekte/optimale Alignment hat. Meist ist das Alignment eines> primitiven Typs ein ganzzahliges Vielfaches von dessen Größe.
Du hast es erfasst.
Wilhelm M. schrieb:>> Das ist zwar richtig, aber eine etwas eigenartige Sichtweise.>> Das ist nicht eigenartig, sondern genau das was meistens passiert.
Nicht das, was passiert, ist eigenartig, sondern die Art, wie du es
beschrieben hast.
Rolf M. schrieb:> Deshalb muss die struct nach c ebenfalls um 3 zusätzliche Füllbytes> erweitert werden.
Was auch passiert. Die Struktur wäre 12 Byte groß, was mit sizeof
verifiziert werden kann.
Je nach Compiler-Options kann die Struktur auch 6 oder 8 Byte groß sein.
A. S. schrieb:> Rolf M. schrieb:>> Deshalb muss die struct nach c ebenfalls um 3 zusätzliche Füllbytes>> erweitert werden.>> Was auch passiert. Die Struktur wäre 12 Byte groß, was mit sizeof> verifiziert werden kann.>> Je nach Compiler-Options kann die Struktur auch 6 oder 8 Byte groß sein.
Es ist schön, wenn jetzt alles gesagt worden ist, und sogar von jedem
;-)
Rolf M. schrieb:> Letztendlich wird die Struktur eben so erweitert, dass auch bei einem> Array daraus jedes Struktur-Element von jedem Array-Element das> korrekte/optimale Alignment hat. Meist ist das Alignment eines> primitiven Typs ein ganzzahliges Vielfaches von dessen Größe.
Es ist eher so, dass das erforderliche Alignment eines primitiven Typs
ein ganzzahliges Vielfaches einer wie auch immer definierten Wortgrösse
der Maschinenarchitektur ist. Wenn die wie bei AVR auf 8 Bits aufbaut,
gibt es keine naheliegende Alignment-Anforderung.
Wilhelm M. schrieb:> Es ist schön, wenn jetzt alles gesagt worden ist
Nö. ;-)
(prx) A. K. schrieb:> Es ist eher so, dass das erforderliche Alignment eines primitiven Typs> ein ganzzahliges Vielfaches einer wie auch immer definierten Wortgrösse> der Maschinenarchitektur ist.
GCC macht bei Cortex-M und uint64_t das Struct-Alignment mit Padding auf
8 Byte. Erforderlich wären nur 4 Byte, aber das bekommt man nur mit
packed hin.
(prx) A. K. schrieb:> Es ist eher so, dass das erforderliche Alignment eines primitiven Typs> ein ganzzahliges Vielfaches einer wie auch immer definierten Wortgrösse> der Maschinenarchitektur ist.
Das kann es auch sein, aber auf den meisten Maschinen ist das keine fixe
Wortgröße, sondern eben abhängig von der Größe des Typs, also wird z.B.
ein 4 Bytes großer Wert auch an 4-Bytes-Grenzen ausgerichtet, während
ein 2-Byte-Wert nur an 2-Byte-Grenzen ausgerichtet wird. Aber die
genauen Aligment-Anforderungen können natürlich je nach Plattform
unterschiedlich sein.
> Wenn die wie bei AVR auf 8 Bits aufbaut, gibt es keine naheliegende> Alignment-Anforderung.
Ja, bei Systemen, wo die native Wortgröße ein Byte ist, ergeben sich
meist keine besonderen Alignment-Anforderungen.
Nop schrieb:> GCC macht bei Cortex-M und uint64_t das Struct-Alignment mit Padding auf> 8 Byte. Erforderlich wären nur 4 Byte, aber das bekommt man nur mit> packed hin.
ARMv7 erfordert 8-Byte Alignment bei LDREXD/STREXD. Diese Befehle gibt
es bei ARMv7-M zwar nicht, könnten aber trotzdem das ABI beeinflussen.