Forum: Mikrocontroller und Digitale Elektronik sizeof mit strukt und padding.


von Holger K. (holgerkraehe)


Lesenswert?

Hallo zusammen

Folgendes Problem:
1
struct bmp_file_header
2
{
3
  uint8_t bfType1;
4
  uint8_t bfType2;
5
  unsigned long bfSize;
6
  unsigned long   bfReserverd;
7
  unsigned long   bfOffset;
8
};
9
10
11
struct bmp_info_header
12
{
13
  unsigned long biSize;
14
  unsigned long biWidth;
15
  unsigned long biHeight;
16
  uint16_t    biPlanes;
17
  uint16_t    biBitCount;
18
  unsigned long biCompression;
19
  unsigned long biSizeImage;
20
  unsigned long biXPelsPerMeter;
21
  unsigned long biYPelsPerMeter;
22
  unsigned long biClrUsed;
23
  unsigned long biClrImportant;
24
};
25
26
....
27
28
struct bmp_info_header * infohead;
29
struct bmp_file_header * filehead;
30
31
infohead= malloc(sizeof(infohead));
32
filehead= malloc(sizeof(filehead));
33
34
35
infohead->....
36
filehead->...


Nun, grundsätzlich funktioniert das obige beispiel.
Jedoch nicht ganz. Weil offenbar überschneiden sich die beiden 
Speicherbereiche. Wenn ich ein member am Anfang von filehead schreibe, 
dann überschreibt es einige Variablen am Ende von infohead.

Offenbar liefert mir sizeof einen zu geringen Wert.
Habe es dann versucht mit der packed definition hinzukriegen.
1
 __attribute__((__packed__))

Leider ohne Erfolg.

Ich verwende den arm-gcc

Hat jemand eine Idee, wie ich mit sizeof genügend Bytes geliefert 
bekomme, damit sich die Bereiche nicht überschneiden?

von Walter T. (nicolas)


Lesenswert?

Du prüfst nicht, ob Malloc liefern kann/einen Nullpointer zurück 
liefert.

von FloMann (Gast)


Lesenswert?

Den sizeof auch wirklich auf die Struktur beziehen nicht auf die 
pointer, je nach mcu haben die
Pointer z.b nur 2Byte oder 4Byte länge.

von Holger K. (holgerkraehe)


Lesenswert?

Danke.

Habs verstanden.

so geht das:
1
sizeof( *(infohead) )

von Rolf M. (rmagnus)


Lesenswert?

Holger K. schrieb:
> so geht das:
> sizeof( *(infohead) )

Die ganze Klammerei kann man auch weglassen:
1
infohead = malloc(sizeof *infohead);

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Warum beziehst du das nicht direkt aufs struct?
1
infohead = malloc(sizeof(struct bmp_info_header));
2
filehead = malloc(sizeof(struct bmp_file_header));
Damit ist viel klarer und ersichtlicher, was du eigentlich willst.

von Rolf M. (rmagnus)


Lesenswert?

Kaj G. schrieb:
> Warum beziehst du das nicht direkt aufs struct?
> infohead = malloc(sizeof(struct bmp_info_header));
> filehead = malloc(sizeof(struct bmp_file_header));
> Damit ist viel klarer und ersichtlicher, was du eigentlich willst.

Finde ich nun gerade nicht. infohead ist ein Zeiger auf irgendwas. Was 
dieses irgendwas ist, sollte mich an dieser Stelle nicht interessieren. 
Wichtig ist an der Stelle nur, dass man genau soviel Speicher allokiert, 
wie für das, worauf infohead zeigt, benötigt wird, unabhängig davon, was 
das nun genau ist. Und sizeof *infoead drückt exakt das aus.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Rolf M. schrieb:
> infohead ist ein Zeiger auf irgendwas.
Richtig. Zu diesem Zeitpunkt ist es ein Zeiger der auf irgendwas im 
irgendwo zeigt. Und dieses irgendwas im irgendwo wird als struct 
bmp_info_header interpretiert.

Rolf M. schrieb:
> Wichtig ist an der Stelle nur, dass man genau soviel Speicher allokiert,
> wie für das, worauf infohead zeigt, benötigt wird,
infohaed zeigt zu diesem Zeitpunkt aber auf nichts, bzw. auf irgendwas.
Die bessere Formulierung waere also:
Wichtig ist an der Stelle nur, dass genau soviel Speicher allokiert 
wird, wie fuer das Objekt, auf das infohead zeigen soll, benoetigt 
wird.

Ich finde deine Einstellung etwas komisch. Legst Du auch erst einen 
int-Pointer an, und wendest darauf sizeof an, oder wendest du sizeof 
direkt auf int an?
1
int *ptr;
2
ptr = malloc(sizeof(*ptr));
3
4
ptr = malloc(sizeof(int));

Mag geschmackssache sein. Soll also jeder machen, wie er mag. :)

Edit:
Muesste sizeof(*ptr) nicht sogar undefined behavior sein? Es wird 
immerhin ein nicht initialisierter Pointer dereferenziert.

von Rolf M. (rmagnus)


Lesenswert?

Kaj G. schrieb:
> Rolf M. schrieb:
>> Wichtig ist an der Stelle nur, dass man genau soviel Speicher allokiert,
>> wie für das, worauf infohead zeigt, benötigt wird,
> infohaed zeigt zu diesem Zeitpunkt aber auf nichts, bzw. auf irgendwas.
> Die bessere Formulierung waere also:
> Wichtig ist an der Stelle nur, dass genau soviel Speicher allokiert
> wird, wie fuer das Objekt, auf das infohead zeigen soll, benoetigt
> wird.

Ja, richtig. Ich denke, es ist klar, dass das so gemeint ist.

> Ich finde deine Einstellung etwas komisch. Legst Du auch erst einen
> int-Pointer an, und wendest darauf sizeof an, oder wendest du sizeof
> direkt auf int an?
> int *ptr;
> ptr = malloc(sizeof(*ptr));
>
> ptr = malloc(sizeof(int));
>
> Mag geschmackssache sein. Soll also jeder machen, wie er mag. :)

Ich lege selten einzelne dynamisch allokierte ints an, aber ja, ich 
schreibe bei malloc praktisch immer sizeof *ptr. Warum auch nicht? Wenn 
das mal statt eines int ein long werden muss, kann ich nicht vergessen, 
das beim malloc auch anzupassen - es passt sich von selber an den Typ 
der Variable an. Das ist für mich der Hauptvorteil daran. Warum soll ich 
auch den Typ nochmal explizit wiederholen, wo doch der Compiler genau 
weiß, von welchem Typ der Zeiger ist?

> Edit:
> Muesste sizeof(*ptr) nicht sogar undefined behavior sein? Es wird
> immerhin ein nicht initialisierter Pointer dereferenziert.

Nein, sizeof ist in ISO C explizt davon ausgenommen, da es sich nicht 
auf den Wert, sondern ausschließlich auf den Typ des übergebenen Objekts 
bezieht. Der Operand, also hier das *ptr wird nicht evaluiert, und damit 
findet keine tatsächliche Dereferenzierung statt.

: Bearbeitet durch User
von Ralph (Gast)


Lesenswert?

Die Größe einer Struktur im physikalischen Ram und der von sizeof 
geliefert Wert muss nicht immer identisch sein.

Sizeof liefert die definierte und nicht die physikalische Größe der 
Struktur.

Ob das passt hängt davon ab wie der Compiler Konfiguriert und die 
Struktur angelegt ist.

Eine Beispiel dazu:
typedef struct TEST
{
   U8  Byte1;
   U32 word1;
}

Steht der Kompiler auf Ramoptimierung:
Größe im Ram  : 5 Byte
Größe Sizeof  : 5 Byte

Steht der Kompiler auf Speedoptimierung:
Größe im Ram  : 8 Byte
Größe Sizeof  : 5 Byte

Warum ?
Recht einfach, der Compiler ergänzt die Struktur in dieser Art:
typedef struct TEST
{
   U8  Byte1;
   U8 Hole1;
   U8 Hole2;
   U8 Hole3;
   U32 word1;
}

Das wird gemacht damit der nächste U32 Byte Zugriff auf einer passende 
Grenze beginnt, und damit der Wert direkt ohne schieben ins Register 
passt.

Man kann die Struktur aber recht einfach so anlegen das hier keine 
Differenzen auftreten.
Indem man die Variablen nach ihrer Größe sortiert in der Stuktur anlegt.

Beispiel:
typedef struct TEST
{
   U32 word1;
   U16 int1;
   U16 int2;
   U8  Byte1;
   U8  Byte2;

}

Kein Element der Struktur muss mit Holes auf eine zur Größe passenden 
Adressgrenze geschoben werden.

So passen die physikalische Größe und der Wert aus Sizeof immer 
zusammen.


Eine andere Frage.
Warum MALLOC ?

Eine dynamische Ramzuweisung hat immer das Potential zu fehlerhaften, 
teilweise nicht reproduzierbaren und unerklärbarem Verhalten der 
Software.
Warum ?
Recht einfach, zur Laufzeit kann es so jederzeit vorkommen das nicht 
ausreichen Ram zur Verfügung steht um gerade die nächste MALLOC 
ausführen zu können. Nach jedem Reset kann das aber an einer anderen 
Stelle passieren, ganz besonders wenn da auch noch Interrupt im Spiel 
sind.
Das zu debuggen ist der Horror

Besser ist es jeder Stuktur und Variablen zur Compilerzeiot eine feste 
Adresse zuzuordnen. Dadurch steht zur Compilezeit schon fest ob 
ausreichend Ram zur Verfügung steht.
Wenn das schon fehlschlägt kann eine dynamische Verwaltung nichts 
retten. höchstens zeitweise überdecken.
Und gerade dann wenn man es nicht brauchen kann schlägt das dann doch 
zu.

Auf Grund dieses nicht vorhersehbaren Verhaltens wird MALLOC in 
sicherheitskritischen Anwendungen, wie zb Automotiv, nicht verwendet.

von Dr. Sommer (Gast)


Lesenswert?

Ralph schrieb:
> Sizeof liefert die definierte und nicht die physikalische Größe der
> Struktur.
Leider genau falsch, sizeof liefert eben doch die physikalische Größe im 
RAM - probier's mit deinem Beispiel aus! Das muss auch so sein, denn 
sonst würde man ja z.B. bei  malloc(sizeof(struct TEST)) zu wenig 
Speicher anfordern. Die Padding-Bytes werden immer mitgezählt.

Ralph schrieb:
> Warum MALLOC ?

Ralph schrieb:
> Besser ist es jeder Stuktur und Variablen zur Compilerzeiot eine feste
> Adresse zuzuordnen.
Tja, das geht nicht immer. Bei PC-Anwendungen hat man oft dynamische 
Datenstrukturen, deren Größe sich erst zur Laufzeit ergibt. Wenn man 
immer das theoretische Maximum von allem allokiert, fordert man eine 
Menge Speicher an und braucht ihn vielleicht gar nicht. Bei 
eingebetteten Systemen lässt sich aber oft vorhersehen, wie viel man 
braucht, und kann es statisch anlegen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Holger K. schrieb:
>
> struct bmp_file_header
> {
>   uint8_t bfType1;
>   uint8_t bfType2;
>   unsigned long bfSize;

Besser: uint32_t, denn da stehen immer 32 Bit. Und dem Leser wird es 
auch klarer; C99 ist's eh schon.

>   unsigned long   bfReserverd;
>   unsigned long   bfOffset;
> };
>
> [...]
>
> Habe es dann versucht mit der packed definition hinzukriegen.

Auch wenn woanders noch ein Fehler war, ist __packed__ dennoch richtig. 
Es gibt zwar Plattformen, die bei diesen Structs weder Paddings 
innerhalb noch am Ende einfügen, aber wenn das nicht so ist suchst du 
dir den Wolf.  Und sizeof beim Kopieren hat evtl. nicht die Größe eines 
Headers, die hier 2 mod 4 ist. Ergo: __packed__.

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.