Forum: PC-Programmierung memory von uint8_t werten in einer Struktur


von Marcel (Gast)


Lesenswert?

einen schönen guten abend,
ich habe eine Struktur mit zwei uint8 elementen
1
typedef struct
2
{
3
  uint8_t a;
4
  uint8_t b;
5
}testStruct_ts;
6
7
int main(void)
8
{
9
  static testStruct_ts testStruct_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/8ub 0x762c70
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?

von (prx) A. K. (prx)


Lesenswert?

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.

von Hmmm (Gast)


Lesenswert?

Marcel schrieb:
> Aber heißt das, dass das Programm für die Elemente in der Struktur immer
> 32Bit speicher vorbehält?

Nicht immer:

https://gcc.gnu.org/onlinedocs/gcc-5.5.0/gcc/Structure-Packing-Pragmas.html

von (prx) A. K. (prx)


Lesenswert?

Hmmm schrieb:
> Marcel schrieb:
>> Aber heißt das, dass das Programm für die Elemente in der Struktur immer
>> 32Bit speicher vorbehält?
>
> Nicht immer:
>
> https://gcc.gnu.org/onlinedocs/gcc-5.5.0/gcc/Structure-Packing-Pragmas.html

Hier unnötig. Der Compiler wird die beiden Bytes direkt hintereinander 
legen, auch ohne dass es erzwungen werden muss.

von (prx) A. K. (prx)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

(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?

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

(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.

von Sebastian (Gast)


Lesenswert?

Marcel schrieb:
> dass die Struktur an der Adresse 0x762C70 liegt

Und an welchen Adressen liegen a und b?

LG, Sebastian

von A. S. (Gast)


Lesenswert?

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.

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

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.html
https://www.kernel.org/doc/Documentation/unaligned-memory-access.txt

von Nop (Gast)


Lesenswert?

Irgend W. schrieb:

> 
https://www.iar.com/knowledge/support/technical-notes/compiler/accessing-unaligned-data/

Da geht es aber um einen 32-bit-Zugriff auf nicht durch vier teilbare 
Adressen. Völlig andere Baustelle. Also LDR und STR.

Im Gegensatz dazu wird ein uint8_t-Zugriff mit LDRB/STRB gemacht, und 
die müssen nicht aligned sein (Hervorhebung von mir):

"An aligned access is an operation where a word-aligned address is used 
for a word, or multiple word access, or where a halfword-aligned address 
is used for a halfword access. Byte accesses are always aligned.

There is no support for unaligned accesses on the Cortex-M0 processor. 
Any attempt to perform an unaligned memory access operation results in a 
HardFault exception."

https://developer.arm.com/documentation/dui0497/a/the-cortex-m0-instruction-set/about-the-instruction-descriptions/address-alignment?lang=en

von (prx) A. K. (prx)


Lesenswert?

Welche CPU hat denn Probleme mit einem misaligned Access in Bytegrösse? 
Denn darum geht es hier ja.

von Wilhelm M. (wimalopaan)


Lesenswert?

Die meisten Compiler erweitern die unterliegenden Struktur-Komponenten 
typischerweise auf die größte der enthaltenen Komponenten:
1
struct {
2
    uint8_t b;
3
    uint32_t a;
4
    uint8_t c;
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
typedef struct {
2
    uint64_t a;
3
    uint16_t b;
4
    uint8_t c;
5
}

Das obige Aggregat benötigt dann wieder oft nur 16 Byte.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

Marcel schrieb:
> Aber heißt das, dass das Programm für die Elemente in der Struktur immer
> 32Bit speicher vorbehält?

https://stackoverflow.com/questions/4306186/structure-padding-and-packing

von A. S. (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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 
;-)

von (prx) A. K. (prx)


Lesenswert?

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ö. ;-)

von Nop (Gast)


Lesenswert?

(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.

von Rolf M. (rmagnus)


Lesenswert?

(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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.