Forum: Mikrocontroller und Digitale Elektronik C -> AVR -> Strukturen -> Padding Bytes


von Jan H. (janiiix3)


Lesenswert?

Moin,

Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?
Hat das was mit der CPU zu tun?
Gibt es das nur bei Strukturen?
1
/*  Struktur & __PACKED__
2
*  Sorgt dafür, dass eine Struktur nicht mit "Padding" Bytes belegt wird.
3
*  
4
*  typedef struct
5
*  {
6
*    uint8_t x;
7
*    uint16_t y;
8
*  }pos_t;
9
*  pos_t Position = [x][Padding][Padding][Padding][Padding][Padding][y][y]
10
*  sizeof(pos_t) = 8 Bytes bei einem "AVR", mit Padding Bytes
11
*  
12
*
13
*  typedef struct __PACKED__
14
*  {
15
*    uint8_t x;
16
*    uint16_t y;
17
*  }pos_t;
18
*  pos_t Position = [x][y][y]
19
*  sizeof(pos_t) = 3 Bytes ( AVR ) // Ohne Padding Bytes
20
*
21
*/
22
#define __PACKED__                ( __attribute__( ( packed ) ) )

von Rolf M. (rmagnus)


Lesenswert?

Jan H. schrieb:
> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?

Die ABI-Definition wäre wohl die Stelle, wo das definiert ist. Meistens 
läuft es darauf hinaus, dass die Adresse beim Zugriff auf einen 
primitiven Datentyp immer ein ganzzahliges Vielfaches von dessen Größe 
sein muss.

> Hat das was mit der CPU zu tun?

Ja. Je nach CPU sind Speicherzugriffe mit falschem Alignment langsamer 
oder gar nicht möglich.

> Gibt es das nur bei Strukturen?

Ja.

von Peter D. (peda)


Lesenswert?

Jan H. schrieb:
> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?
> Hat das was mit der CPU zu tun?

Ja. Der AVR ist ein 8Bitter, der fügt also nichts ein.
Beim ARM kannst Du Dir mit sizeof die Größe einer Struct in Bytes 
ausgeben lassen.

von g457 (Gast)


Lesenswert?

> Je nach CPU sind Speicherzugriffe mit falschem Alignment langsamer
> oder gar nicht möglich.

Oder die niederwertigsten Adressbits werden stillschweigend ignoriert 
(und man bekommt deshalb Daten aus dem falschen Speicherbereich).

von Jan H. (janiiix3)


Lesenswert?

Rolf M. schrieb:
> Jan H. schrieb:
>> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?
>
> Die ABI-Definition

Für was steht das?
Wo finde ich das?

Wenn man jetzt eine Struktur hat wie z.B
1
struct
2
{
3
   uint8_t x;
4
   uint8_t y;
5
}test_t;

Müsste diese jetzt auf einem AVR ( 8 Bit ) 2 Byte groß sein?

von Einer K. (Gast)


Lesenswert?

Jan H. schrieb:
> Müsste diese jetzt auf einem AVR ( 8 Bit ) 2 Byte groß sein?
Wurde schon beantwortet.

Jan H. schrieb:
> Wo finde ich das?
Falsche Frage!
Die Antwort lautet:
Im Internet!

Eigentlich muss sie heißen:
Wie finde ich das?

Und die Antwort lautet:
In dem man danach sucht!

Unter 3 Minuten suchen:
https://gcc.gnu.org/wiki/avr-gcc#ABI

von fop (Gast)


Lesenswert?

Rolf M. schrieb:
>> Gibt es das nur bei Strukturen?
>
> Ja.

Nein! Auch zwischen zwei völlig unabhängigen Variablen kann der Linker 
Leerraum lassen, damit der Zugriff besser flutscht.

Bei einer Struktur könnte man mit sizeof() rausfinden, wieviel Platz 
frei gelassen wurde.
Wenn man keine Struktur hat oder genauer wissen will wo, gäbe es noch 
die Möglichkeit einen Pointer auf einen Integerwert zu casten und da 
etwas heraus zu lesen.
Oder wenn es nur Dich interressiert : lies doch mal aufmerksam die 
.map-Datei durch.

Meine Meinung zu dem Thema ist :
Vernünftige Software sollte einfach funktionieren, egal wo ihre 
Variablen im Speicher liegen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Jan H. schrieb:
>> Hat das was mit der CPU zu tun?
>
> Ja. Je nach CPU sind Speicherzugriffe mit falschem Alignment langsamer
> oder gar nicht möglich.

Für alle AVR-CPUs genügt ein Alignment von 1 Byte, und die 
Zugriffsgeschwindigkeit hängt bei AVRs nicht vom Alignment ab.  Ich lass 
mich da aber gerne eines besseren belehren.

Jan H. schrieb:
> Moin,
>
> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?
> Hat das was mit der CPU zu tun?
> Gibt es das nur bei Strukturen?
>
>
1
> /*  Struktur & __PACKED__
2
> *  Sorgt dafür, dass eine Struktur nicht mit "Padding" Bytes belegt 
3
> wird.
4
> *
5
> *  typedef struct
6
> *  {
7
> *    uint8_t x;
8
> *    uint16_t y;
9
> *  }pos_t;
10
> *  pos_t Position = 
11
> [x][Padding][Padding][Padding][Padding][Padding][y][y]
12
> *  sizeof(pos_t) = 8 Bytes bei einem "AVR", mit Padding Bytes
13
14
Ich bekomme hier 3 als Größe.  Der Folgende Code compilier nämlich ohne Fehler:
15
16
[c]
17
#include <stdint.h>
18
19
typedef struct
20
{
21
    uint8_t x;
22
    uint16_t y;
23
} pos_t;
24
25
int a[sizeof (pos_t) == 3 ? 1 : -1];

Compiliert mit avr-gcc [-Os] -fno-pack-struct und verschiedene 
Compilerversionen (v8, v6.4.1, v4.7.2, WinAVR-20100110 (v4.3.3)).

Wie es für andere AVR C-Compiler aussieht weiß ich nicht.

Das AVR-Backend des GCC macht keine Änderungen am Default, und 
GCC-Default bestimmt Paddings anhand der Größe eines Words:
1
/* Width of a word, in units (bytes).  */
2
#define UNITS_PER_WORD 1

http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr.h?view=markup#l114

Ein formales ABI gab es für AVR nicht, d.h. das ABI des avr-gcc / 
avr-g++ definiert sich dadurch, wie Denis damals das Backend 
implementierte.  ABI-Dokus wie

http://gcc.gnu.org/wiki/avr-gcc

entstanden erst viel später, also reverse engineered.  Und zwar anhand 
dessen, was avr-gcc konkret implementiert.  Layout von Structs / Unions 
wird im GCC-Wiki allerdings nicht behandelt.

von Jan H. (janiiix3)


Lesenswert?

Habe ich das jetzt richtig verstanden?
Wenn man jetzt ein 32 Bit System nutzen würde und ich in einer Struktur 
jetzt nur ein Byte reservieren würde, würde er die restlichen 3 Bytes 
mit Padding Bytes füllen?
1
struct u32
2
{
3
   uint8_t x;
4
}

von Einer K. (Gast)


Lesenswert?

sizeof() würde es dir zeigen.

Wieso stellst du solche Fragen?

von Oliver S. (oliverso)


Lesenswert?

Wenn danach noch weitere 1-Byte—Variablen kommen, nicht unbedingt. Wenn 
danach ein 32—Bit—Wert kommt, bestimmt.

Oliver

von Stefan F. (Gast)


Lesenswert?

Jan H. schrieb:
> Habe ich das jetzt richtig verstanden?
> Wenn man jetzt ein 32 Bit System nutzen würde und ich in einer Struktur
> jetzt nur ein Byte reservieren würde, würde er die restlichen 3 Bytes
> mit Padding Bytes füllen?
> struct u32
> {
>    uint8_t x;
> }

Kommt wie gesagt auch die ABI an. Aus dem Bauch heraus würde ich sagen, 
ja meistens trifft deine Aussage zu.

von Punkt. (Gast)


Lesenswert?

So, um es auf den Punkt zu bringen:
Das Ganze ist vom jeweiligen Compiler abhängig.
Wenn man es also richtig machen will, darf man die Größe der Struktur 
nicht selber berechnen. sizeof wurde ja schon gennant.

von Rolf M. (rmagnus)


Lesenswert?

fop schrieb:
> Rolf M. schrieb:
>>> Gibt es das nur bei Strukturen?
>>
>> Ja.
>
> Nein! Auch zwischen zwei völlig unabhängigen Variablen kann der Linker
> Leerraum lassen, damit der Zugriff besser flutscht.

Bei völlig unabhängigen Variablen ergibt sich die Frage nicht, eben weil 
sie völlig unabhängig von einander sind. Die können auch komplett anders 
sortiert sein als im Quellcode. So landen z.B. mit 0 initialisierte 
globale Variablen oft in ganz anderen Speicherbereichen als welche, die 
mit einem anderen Wert initialisiert werden. Auch ein "packing" kann man 
über mehrere Variablen, die nichts miteinander zu tun haben, nicht 
machen.

> Meine Meinung zu dem Thema ist :
> Vernünftige Software sollte einfach funktionieren, egal wo ihre
> Variablen im Speicher liegen.

Ja. Allerdings will Software manchmal auch mit der Umwelt kommunizieren, 
und muss man sich ggf. mit solchen Themen beschäftigen.

Johann L. schrieb:
> Für alle AVR-CPUs genügt ein Alignment von 1 Byte, und die
> Zugriffsgeschwindigkeit hängt bei AVRs nicht vom Alignment ab.  Ich lass
> mich da aber gerne eines besseren belehren.

Das stimmt. Ich hatte übersehen, dass es um AVR geht, weil das im Text 
des Postings nicht erwähnt wurde.

> Compiliert mit avr-gcc [-Os] -fno-pack-struct und verschiedene
> Compilerversionen (v8, v6.4.1, v4.7.2, WinAVR-20100110 (v4.3.3)).

Warum schaltest du das Packing denn explizit aus?

Jan H. schrieb:
> Habe ich das jetzt richtig verstanden?
> Wenn man jetzt ein 32 Bit System nutzen würde und ich in einer Struktur
> jetzt nur ein Byte reservieren würde, würde er die restlichen 3 Bytes
> mit Padding Bytes füllen?
> struct u32
> {
>    uint8_t x;
> }

Nicht gezwungenermaßen. Aber bei sowas:
1
struct u32
2
{
3
   uint32_t y;
4
   uint8_t x;
5
}
Denn man kann ja auch ein Array aus diesen Strukturen machen. Bei Arrays 
gibt's aber kein Padding. Damit bei einem u32[2] das y vom zweiten 
Element auch aligned ist, müssen nach dem x vom ersten aber drei 
Padding-Bytes kommen, die Teil der struct sein müssen. Also wird diese 
struct auf gängigen 32-Bit-Systemen vermutlich 64 Bit groß sein.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Rolf M. schrieb:
> Bei völlig unabhängigen Variablen ergibt sich die Frage nicht,
> eben weil sie völlig unabhängig von einander sind. Die können
> auch komplett anders sortiert sein als im Quellcode.

Naja, wenn ich zwei globale Variablen definiere und gleich 
initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary 
auf.

Und auch dort darf der Compiler padding einführen (und tut es auf 
manchen Architekturen auch, z.B. ia64).

von Rolf M. (rmagnus)


Lesenswert?

S. R. schrieb:
> Rolf M. schrieb:
>> Bei völlig unabhängigen Variablen ergibt sich die Frage nicht,
>> eben weil sie völlig unabhängig von einander sind. Die können
>> auch komplett anders sortiert sein als im Quellcode.
>
> Naja, wenn ich zwei globale Variablen definiere und gleich
> initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary
> auf.

Wie gesagt: Wenn du z.B. die eine mit 0 initialisierst und die andere 
mit 1, ist die Wahrscheinlichkeit sehr gering, dass sie nebeneinander 
landen.

> Und auch dort darf der Compiler padding einführen (und tut es auf
> manchen Architekturen auch, z.B. ia64).

Ich würde hier nicht von Padding sprechen. Davon spricht man meiner 
Meinung, wenn zwischen zusammengehörigen Daten Füllbytes einführt. Wenn 
die alle separat sind, wird einfach für jede einzeln geschaut, dass sie 
mit passendem Alignment im Speicher liegt. Gut, wenn die alle 
hintereinander im Speicher landen, ist das Prinzip natürlich sehr 
ähnlich, aber der Compiler bzw. Linker könnte sich genauso gut 
entscheiden, alle globalen 4-Byte-Variablen in einen gemeinsamen 
Speicherblock zu legen und alle 2-Byte-Variablen in einen anderen, um 
möglichst wenig Verschnitt zu haben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

S. R. schrieb:
> Rolf M. schrieb:
>> Bei völlig unabhängigen Variablen ergibt sich die Frage nicht,
>> eben weil sie völlig unabhängig von einander sind. Die können
>> auch komplett anders sortiert sein als im Quellcode.
>
> Und auch dort darf der Compiler padding einführen (und tut es auf
> manchen Architekturen auch, z.B. ia64).

Rein technisch ist das kein Padding.  Ein Padding wäre Teil der internen 
Repräsentation, GCC erreicht das z.B. mit .space.

Was du meinst sind die Lücken, die durch das Alignment von Objekten 
entstehen, diese werden bei dem GNU-Tools vom Locator eingefügt.  Der 
Compiler kann hier nix einfügen, da er keine Adressen kennt. Was der 
Compiler einfügt sind Direktiven wie .p2align N vor einem Object, damit 
dessen Startadresse durch 2**N teilbar ist.  Alternative Direktive ist 
.balign 2**N.  (Semantik von .align ist abhängig vom Target wie .p2align 
oder wie .balign.  Mehr Argumente sind möglich, z.B. für Maximalanzahl 
einzufügender Bytes, wie sie gerne bei Code-Alignment zum Einsatz 
kommen.)

Will man den Verschnitt aufgrund Alignment minimieren, kann man im 
Linker-Script anweisen, die Objekte nach ihrem Alignment sortieren zu 
lassen.

> Naja, wenn ich zwei globale Variablen definiere und gleich
> initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary
> auf.

Es soll schon Spezialisten gegeben haben, die sich auf eine bestimmte 
Reihenfolge der Objekte verlassen haben, und mit der nächsten 
Binutils-Version hat plötzlich nix mehr funktioniert.  Grund: Binutils 
verwendete intern andere Algorithmen, so dass die Objekte in anderer 
Reihenfolge ausgegeben wurden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Johann L. schrieb:
>> Compiliert mit avr-gcc [-Os] -fno-pack-struct und verschiedene
>> Compilerversionen (v8, v6.4.1, v4.7.2, WinAVR-20100110 (v4.3.3)).
>
> Warum schaltest du das Packing denn explizit aus?

Manche GUI setzt ungefragt -fpack-struct. Wenn man das Beispiel in so 
einer Umgebung testen würde, wäre es nichtssagend wegen -fpack-struct. 
Daher als Erinnerung dass man den Default für einen sinnvollen Test 
braucht.

von S. R. (svenska)


Lesenswert?

Rolf M. schrieb:
>> Naja, wenn ich zwei globale Variablen definiere und gleich
>> initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary
>> auf.
>
> Wie gesagt: Wenn du z.B. die eine mit 0 initialisierst und die andere
> mit 1, ist die Wahrscheinlichkeit sehr gering, dass sie nebeneinander
> landen.

Darum schrieb ich "gleich initialisiere", so dass beide entweder in .bss 
oder in .data landen.

Wenn ich die eine mit 1 und die andere mit 2 initialisiere, sieht das 
mit der Wahrscheinlichkeit wieder völlig anders aus...

>> Und auch dort darf der Compiler padding einführen (und tut es auf
>> manchen Architekturen auch, z.B. ia64).
> Ich würde hier nicht von Padding sprechen.

Ich meinte, dass einzelne Variablen z.B. immer auf 128 Bit aufgeblasen 
werden (unabhängig vom Datentyp), damit der Zugriff darauf effizienter 
ist. Das ist im Prinzip auch eine Art, Füllbytes einzuführen, aber auf 
einer anderen Ebene. Wenn du das anders bezeichnen möchtest, dann sehe 
ich das auch ein.

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.