Forum: Compiler & IDEs packed bei verschachtelten Strukturen


von Rainer K. (Gast)


Lesenswert?

Hallo Liebe Community,

zuerst ein Beispiel:
1
typedef struct{
2
 uint8_t one;
3
 uint8_t two;
4
 uint8_t three;
5
} MEMBER_STRUCT;
6
7
struct myStruct{
8
 uint32_t member1;
9
 uint8_t  member2;
10
 MEMBER_STRUCT member3;
11
}__attribute__((__packed__));
12
typedef struct myStruct MYSTRUCT;
13
14
MYSTRUCT instanceVariable;

Ohne jetzt drüber zu diskutieren das ich hier unaligned 
Speicherzugriffen machen muss... Was passiert mit member3? Wenn ich 
MEMBER_STRUCT getrennt von myStruct verwende wird vermutlich ein Byte 
gepadded, da so ein Langwortzugriff möglich ist. Wir das padding Byte 
bei member3 auch eingefügt, selbst wenn es eine "Unter-"Struct einer 
packed Struct ist?

Danke und Gruß, Rainer

von Karl H. (kbuchegg)


Lesenswert?

Rainer K. schrieb:

> Ohne jetzt drüber zu diskutieren das ich hier unaligned
> Speicherzugriffen machen muss... Was passiert mit member3? Wenn ich
> MEMBER_STRUCT getrennt von myStruct verwende wird vermutlich ein Byte
> gepadded, da so ein Langwortzugriff möglich ist. Wir das padding Byte
> bei member3 auch eingefügt, selbst wenn es eine "Unter-"Struct einer
> packed Struct ist?


Hmm
Gute Frage.
Der C-Standard hält sich bei derartigen Sachen sehr bedeckt. Ausser "Ja, 
es kann Padding geben" ist da sehr wenig definiert.

Hast du schon versucht mittels sizeof zu Erkentnissen zu kommen, wie das 
dein Compiler handhabt?

von Rainer K. (Gast)


Lesenswert?

Habe den sizeof Test mal gemacht, ich hatte  diesen vor geraumer Zeit 
mit einer älteren GCC Version schon durchgeführt und ich bin mir sicher 
das dort alle Padding-Bytes beseitigt wurden, auch bei Unterstrukturen. 
Erstaunlicher Weise ist das bei meiner aktuellen Version 4.4.1 nicht der 
Fall. In dem konkreten Beispiel wird member3 auf 4 Byte aufgeblasen.

Einmal öfter zeigt sich das man möglichst Compilerunabhänigen Code 
schreiben sollte, dies ist aber in der Realität oft nur bedingt möglich. 
:(

Danke für den Hinweis. :)

von Andreas B. (Gast)


Lesenswert?

Ich würde mal sagen, dass das Padding von MEMBER_STRUCT durch das packed 
auf myStruct nicht beinflusst wird und das so das einzige ist, was Sinn 
macht. Was sollte sonst der Compiler bei einem Zugriff über Zeiger auf 
MEMBER_STRUCT machen? Das kann ja nicht davon abhängen wo das 
MEMBER_STRUCT liegt.

Ich seh grad, dass das auch explizit in der gcc-Dokumentation steht:

>     In the following example `struct my_packed_struct''s members are
>     packed closely together, but the internal layout of its `s' member
>     is not packed--to do that, `struct my_unpacked_struct' would need
>     to be packed too.
>
>          struct my_unpacked_struct
>           {
>              char c;
>              int i;
>           };
>
>          struct _attribute_ ((_packed_)) my_packed_struct
>            {
>               char c;
>               int  i;
>               struct my_unpacked_struct s;
>            };

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Rainer K. schrieb:
> Was passiert mit member3? Wenn ich MEMBER_STRUCT getrennt von
> myStruct verwende wird vermutlich ein Byte gepadded, da so ein
> Langwortzugriff möglich ist.

Das kommt auf die Architektur an. Hast Du eine spezielle im Sinn?

Andreas B. schrieb:
> Ich würde mal sagen, dass das Padding von MEMBER_STRUCT durch das
> packed auf myStruct nicht beinflusst wird und das so das einzige
> ist, was Sinn macht.

Ist aber auch nicht so intuitiv. Du hast dann u.U. padding zwischen
einem Element der umgebenden packed Struktur und dem unpacked
Strukturelement. Zu wem gehört das Padding jetzt? Zur umgebenden
Struktur kann es nicht gehören, da diese per Definition kein Padding
enthalten kann. Zum unpacked Element kann es auch nicht gehören, da
sich dann sizeof(member) ändern würde.

Die richtige Antwort des Compilers ist diese:
1
"packed_structs.c", line 83: Error:  #1032: Definition of nested
2
        anonymous struct in packed "struct myStruct" must be __packed
3
   MEMBER_STRUCT member3;
4
                 ^
5
packed_structs.c: 0 warnings, 1 error

Gruß
Marcus

von sebastians (Gast)


Lesenswert?

>Zum unpacked Element kann es auch nicht gehören, da
>sich dann sizeof(member) ändern würde.
Wieso "ändern"? Gegenüber welcher Referenzgröße?
sizeof(MEMBER_STRUCT) ist 4 wenn man es im normalen Kontext verwendet, 
also gehört das pad-byte zu MEMBER_STRUCT, wo ist das Problem?

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

sebastians schrieb:
>>Zum unpacked Element kann es auch nicht gehören, da
>>sich dann sizeof(member) ändern würde.
> Wieso "ändern"? Gegenüber welcher Referenzgröße?

Dem alleinstehenden MEMBER_STRUCT. sizeof(MEMBER_STRUCT) wäre dann
nicht mehr gleich sizeof(MYSTRUCT.member3).

> sizeof(MEMBER_STRUCT) ist 4 wenn man es im normalen Kontext verwendet,
> also gehört das pad-byte zu MEMBER_STRUCT, wo ist das Problem?

Wenn man davon ausgeht, dass MEMBER_STRUCT ein abschließendes Padding
Byte enthält, "da so ein Langwortzugriff möglich ist", dann gelten
möglicherweise auch irgendwelche Einschränkungen bezüglich des
Alignments. Nehmen wir also mal an, das Alignment von MEMBER_STRUCT
wäre nicht beliebig (!=1). Dann hinge es vom Alignment der Elemente in
myStruct ab, ob Padding zwischen member2 und member3 eingefügt
wird. Die Anzahl der Padding Bytes müsste unter Umständen sogar für
verschiedene Instanzen von MYSTRUCT unterschiedlich sein, abhängig
davon, an welcher Adresse member2 tatsächlich liegt. Es sei denn, dass
Alignment von member3 bestimmt das Alignment von MYSTRUCT, was
wiederum dem Gedanken von packed widerspricht.

Gruß
Marcus

von Rainer K. (Gast)


Lesenswert?

Hmm also GCC padded bei "nested structs", der KEIL Compiler aber sehr 
wohl. Keiner von beiden gibt eine Warning oder gar error aus. Unschön 
das es hier eine Diskrepanz gibt.

Und ganz intuitiv finde ich das Verhalten vom GCC nicht, denn ich dachte 
eigentlich, das er bei einem typedef im grunde genommen die variablen 
der inneren Struct (member3) quasi an die entsprechende Stelle kopiert, 
quasi soetwas baut:
1
struct myStruct{
2
 uint32_t member1;
3
 uint8_t  member2;
4
 uint8_t one;
5
 uint8_t two;
6
 uint8_t three;
7
}__attribute__((__packed__));
8
typedef struct myStruct MYSTRUCT;

Sollte Member 3 noch tiefer durch typedefs verschachtelt sein, würde ich 
erwarten das er soweit auflöst bis er auf die integralen Datentypen 
kommt. Folglich hätte man nur noch (reell) eine Struktur die aus 
integralen Datentypen besteht, wenn ich diese jetzt packed deklariere 
gibt es kein Padding. Das würde ich intuitiv erwarten. GCC tut es nicht. 
Danke an der Stelle an Andreas B. für das posten der Stelle aus der GCC 
Doku. :)

Ich habe in Ansi-c Standard geschaut, da steht quasi nicht viel zu - es 
wird quasi als Compiler dependent definiert.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Rainer K. schrieb:
> Hmm also GCC padded bei "nested structs", der KEIL Compiler aber sehr
> wohl.

?

> Keiner von beiden gibt eine Warning oder gar error aus.

Was ist denn "der KEIL Compiler"? Die von mir zitierte Fehlermeldung
stammt aus dem Keil MDK-ARM.

> Sollte Member 3 noch tiefer durch typedefs verschachtelt sein, würde ich
> erwarten das er soweit auflöst bis er auf die integralen Datentypen
> kommt. Folglich hätte man nur noch (reell) eine Struktur die aus
> integralen Datentypen besteht, wenn ich diese jetzt packed deklariere
> gibt es kein Padding. Das würde ich intuitiv erwarten.

Aber das kann doch gar nicht gehen. Member3 wäre dann nicht mehr
typkompatibel zu anderen Instanzen von MEMBER_STRUCT.

Gruß
Marcus

von Karl H. (kbuchegg)


Lesenswert?

Rainer K. schrieb:

> kommt. Folglich hätte man nur noch (reell) eine Struktur die aus
> integralen Datentypen besteht, wenn ich diese jetzt packed deklariere
> gibt es kein Padding. Das würde ich intuitiv erwarten.

Ich nicht.

Denn ich erwarte eigentlich, dass
1
typedef struct{
2
 uint8_t one;
3
 uint8_t two;
4
 uint8_t three;
5
} MEMBER_STRUCT;
6
7
struct myStruct{
8
 uint32_t member1;
9
 uint8_t  member2;
10
 MEMBER_STRUCT member3;
11
}__attribute__((__packed__));
12
typedef struct myStruct MYSTRUCT;
13
14
  MYSTRUCT       mitMember;
15
  MEMBER_STRUCT  nurMember;
16
17
  memcpy( &mitMember.member3, &nurMember, sizeof( MEMBER_STRUCT ) );
18
19
  memcpy( &nurMember, &mitMember.member3, sizeof( MEMBER_STRUCT ) );

korrekt funktioniert.
Das hinzufügen eines packed Attributes darf daran nichts ändern. 
Schliesslich habe ich es immer mit der gleichen MEMBER_STRUCT zu tun.

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.