C Array Payload (size as needed)?
Moin,
Unser Professor hat uns letztens in der Vorlesung gezeigt, wie man ein Struct mit beliebig viel Inhalt erstellen kann. Das Struct hatte als Inhalt ein char-Array mit einem Platz (char data[1];). Dann konnte man mit malloc() beliebig viel Speicherplatz zuweisen und weil es in C ja keine Bereichsüberprüfung gibt wie in Java, konnte man dann in data so viel Speichern, wie man will. Aber, wenn man in data jetzt z.B einen Integer oder einen Pointer speichern will, muss man data ja auch irgendwie casten. Das habe ich nicht ganz verstanden. Hier der Code:
typedef struct {
int size;
unsigned char data[1];
}* ObjRef
ObjRef a;
a = malloc(sizeof(ObjRef) + sizeof(int));
*(int*) a -> data = 5;
*(int*) a -> data = 5; ~ Diese Zeile verstehe ich nicht ganz, wäre nice wenn mir jemand erklären könnte, warum man hier so casten muss.
3 Antworten
Schau Dir erstmal das heir an:
https://en.wikipedia.org/wiki/Flexible_array_member
Das wäre die Variante, wie sie in C99 Teil des Standards wurde. Zuvor mußte es eben ein Array der Größe 1 sein und nein einfach ein char pointer tut es nicht, denn data soll kein pointer sein, sondern ein beliebig großes Feld von Bytes (chars).
struct {
int size;
unsigned char data[1];
}
Hier werden die Daten quasi in die struct eingebettet.
struct {
int size;
unsigned char *;
}
Hier würde data auf einen dedizierten Speicherbereich zeigen, den ich z.B. auch mit realloc verändern kann.
Kommen wir zur eigentlichen Frage.
a -> data <=> (*a).data
Ein Array wird allgemein durch seind Basisadresse dargestellt data ist also die Speicheradresse an der das Array innerhalb der structure liegt.
Ich caste mir die also zu einem int pointer um und dereferenziere dann, weil ich ja an dieser Adresse den Wert ablegen will.
Verlassen wir mal kurz den generischen Bereich und nehmen ein intuitiveres(?) Beispiel:
struct vect{
int x;
int y;
int z;
};
struct vect vectors[100];
Soweit so klar, ich habe ein Array, das 100 vect beinhaltet, doch was, wenn die Zahl nicht fix ist?
struct varr{
int len;
strcut vect vector[];
};
struct varr *myvecs=malloc(sizeof(struct varr)+100*sizeof(struct vect));
myvecs->len=100;
for (int i=0;i<100;++i) myvecs->vector[i].x=1;
Na und wenn ich verschiedene Typen haben will, dann nehme ich eben ein array von chars und muß das für den konkreten Typ zurechtcasten.
Und ich möchte noch etwas verlinken:
Zunächst mal ist a->data in C ein Array. Das ganze geht eben nur wegen der Pointer Array dualität in C. Wesentlichen besser wäre es hier data nicht als Array sondern als unsigned char* data; zu definieren.
*(int*)a->data sagt jetzt caste den unsigend char Pointer data aus der struct a als integer Pointer. Anschließend wird der Integer Pointer dereferenziert wodurch er zu einem int wird.
Ist zwar möglich aber kann schnell zu schwer findbaren Fehlern führen, daher am besten nicht angewöhnen.
Fang mal langsam an:
- char c; // reserviert ein Byte.
- char * cp = &c; // ist dessen Adresse.
- int *ip = (int *) cp; // ist dieselbe Adresse, aber „geeignet“ für int-Werte.
- *ip = 42; überschreibt das Byte (und noch mehr!) an dieser Adresse.
Alles in einem Rutsch: *(int*)&c = 42;
Das geht prinzipiell mit jedem Datentyp. Nimmt man in 1. statt char ein Array char[1],dann spart man sich das '&' in Punkt 2, weil ein Arraybezeichner (fast) immer automatisch in einen Zeiger auf das erste Element umgewandelt wird.