Eine an eine Funktion übergebene Struktur wird kopiert. Das ist für einen Punkt aus zwei ints in Ordnung und für einen 200-Byte-Datensatz verschwenderisch - und es macht Änderungen unmöglich, denn die Funktion sieht immer nur ihre eigene Kopie. Auf eine Struktur zu zeigen löst beide Probleme auf einmal, und es ist auch der einzige Weg, Strukturen zu bauen, die wachsen: Listen, Bäume, Graphen.
Einen Zeiger auf eine Struktur nehmen
Es braucht nichts Besonderes. & gibt dir die Adresse, und der Typ ist „Zeiger auf Struktur":
Alle drei geben 3 aus. Die letzten beiden sind dieselbe Operation in zwei Schreibweisen.
Warum (*ptr).x seine Klammern braucht
Du könntest versuchen, sie wegzulassen und *ptr.x zu schreiben. Das übersetzt sich zu etwas ganz anderem oder verweigert öfter noch die Übersetzung ganz - denn der Elementoperator . hat höheren Vorrang als die Dereferenzierung *:
*ptr.x // liest sich als *(ptr.x) -- falsch
(*ptr).x // zuerst dereferenzieren, dann das Element nehmen -- korrekt
ptr->x // dasselbe, kurz gesagt
ptr.x fragt nach einem Element des Zeigers, und ein Zeiger hat keine Elemente. Die Beschwerde des Compilers („request for member 'x' in something not a structure or union") ist ein getarnter Vorrangfehler.
Weil diese geklammerte Form hässlich und leicht falsch zu machen ist, bietet C -> an. In der Praxis wirst du (*p).x in echtem Code fast nie sehen; greif zu p->x und vergiss, dass es die lange Form gibt - außer als Erklärung dafür, was der Pfeil bedeutet.
Eine Struktur per Zeiger übergeben
Ein Zeigerparameter gibt der Funktion die tatsächliche Struktur des Aufrufers, Änderungen bleiben also bestehen:
Zwei Gewohnheiten lohnen sich hier:
- Markiere nur lesende Zeiger als
const.const struct Point *pverspricht, dass die Funktion nicht ändert, woraufpzeigt. Fügt jemand später einp->x = 0darin ein, scheitert die Übersetzung, statt dass der Bug ausgeliefert wird. Es dokumentiert die Absicht auch an der Aufrufstelle. - Übergib große Strukturen auch beim Lesen per Zeiger. Eine Struktur mit einem 64-Byte-Namenspuffer kostet pro Wertübergabe 64 Bytes Kopie; ein Zeiger kostet 8.
Kleine Strukturen darf man weiterhin als Wert übergeben - struct Point mit 8 Bytes kopiert sich so billig wie ein Zeiger, und die Wertfassung kann nicht NULL sein, was eine ganze Fehlerart beseitigt.
Zeiger in Arrays von Strukturen
Zeigerarithmetik funktioniert auf Strukturarrays genauso wie auf int-Arrays: p + 1 rückt um eine ganze Struktur vor, Füllbytes inklusive.
Ein Arrayname zerfällt zu einem Zeiger auf sein erstes Element, staff ist beim Aufruf also bereits ein struct Employee *. Deshalb braucht die Funktion auch ein separat übergebenes n - sizeof würde innerhalb der Funktion den Zeiger messen, nicht das Array.
Eine Struktur auf dem Heap belegen
Strukturen auf dem Stack sterben am Ende ihres Gültigkeitsbereichs. Um eine zu erzeugen, die die Funktion überlebt, die sie erstellt hat - oder so viele, wie die Eingabe verlangt -, belege mit malloc:
Vier Einzelheiten in dieser kleinen Funktion sind alle tragend:
sizeof *e, nichtsizeof(struct Employee). Es liest sich als „die Größe dessen, woraufezeigt", ändert sich der Typ also je, folgt die Belegung automatisch. Die beiden können nicht auseinanderdriften.- Prüfe auf
NULL.mallocliefertNULL, wenn es die Anfrage nicht erfüllen kann.e->idüber einen Nullzeiger zu schreiben ist ein Segmentierungsfehler. - Initialisiere jedes Element.
mallocnullt den Speicher nicht; die Struktur kommt voller Müll an. (calloc(1, sizeof *e)nullt sie für dich.) - Irgendjemand muss sie mit
freefreigeben. Einen belegten Zeiger zurückzugeben überträgt diese Pflicht an den Aufrufer. Schreib es in einen Kommentar - eine unklare Besitzregel ist der Anfang von Speicherlecks.
Der Gewinn: ein Knoten einer verketteten Liste
Hier ist das, was Strukturen ohne Zeiger nicht können. Eine Struktur kann sich nicht selbst enthalten - das wäre unendlich groß -, aber sie kann einen Zeiger auf ihresgleichen enthalten, und dieser eine Trick baut jede verkettete Datenstruktur in C.
Beachte, dass struct Node *next; sich auf struct Node bezieht, während struct Node noch deklariert wird. Das ist genau deshalb erlaubt, weil ein Zeiger eine bekannte Größe hat, egal worauf er zeigt - der Compiler braucht die vollständige Definition noch nicht. Es ist auch die eine Stelle, an der du das Tag struct Node benutzen musst, selbst wenn du zusätzlich ein typedef geschrieben hast: Der typedef-Name existiert innerhalb seiner eigenen Definition noch nicht.
Die Schleife in free_list sichert head->next, bevor sie free(head) aufruft. head->next nach dem Freigeben zu lesen ist ein Use-after-free - der Speicher gehört dir nicht mehr, und der gelesene Wert ist das, was der Allokator dort abgelegt hat.
Häufige Fehler
p->xauf einem uninitialisierten oderNULL-Zeiger. Die häufigste Absturzursache in strukturlastigem Code. Initialisiere Zeiger mitNULLund prüfe vor dem Dereferenzieren.- Einen Zeiger auf eine lokale Struktur zurückgeben.
struct Point *bad(void) { struct Point p = {1,2}; return &p; }liefert die Adresse von Speicher, der in dem Moment aufhört zu existieren, in dem die Funktion zurückkehrt. Gib die Struktur als Wert zurück oder belege sie. - Doppelt freigeben oder das Freigeben vergessen. Jedes
mallocpaart sich mit genau einemfree. Setze den Zeiger nach dem Freigeben aufNULL, damit eine spätere versehentliche Nutzung laut abstürzt, statt still Speicher zu beschädigen. - Eine Struktur kopieren, die einen Zeiger enthält. Die Strukturzuweisung kopiert den Wert des Zeigers, nicht das, worauf er zeigt. Beide Strukturen verweisen nun auf denselben Puffer, und welche ihn zuerst freigibt, lässt die andere baumeln.
Häufig gestellte Fragen
Was macht der Operator -> in C?
-> in C?p->x greift über den Zeiger p auf das Element x zu. Es ist exakt gleichwertig mit (*p).x - den Zeiger dereferenzieren und dann das Element nehmen. Den Pfeil gibt es, weil Zeiger auf Strukturen so verbreitet sind, dass die längere Form den Code überfrachtet.
Warum funktioniert *p.x bei einem Strukturzeiger nicht?
*p.x bei einem Strukturzeiger nicht?Weil . stärker bindet als *. *p.x liest sich als *(p.x) - nimm das Element x von p (das ein Zeiger ist, keine Struktur) und dereferenziere das. Du brauchst (*p).x oder schreibst einfach p->x.
Sollte ich eine Struktur in C als Wert oder per Zeiger übergeben?
Übergib per Zeiger, wenn die Funktion die Struktur des Aufrufers ändern muss oder wenn die Struktur groß ist und das Kopieren verschwenderisch wäre. Übergib kleine, nur lesend benutzte Strukturen als Wert, wo die Kopie billig und der Code einfacher ist. Markiere nur lesende Zeigerparameter als const struct T *, damit der Compiler das Versprechen durchsetzt.
Wie belegt man eine Struktur in C mit malloc?
struct Node *n = malloc(sizeof *n); - die Form sizeof *n fragt nach der Größe dessen, worauf n zeigt, sie bleibt also korrekt, wenn du später den Typ änderst. Prüfe das Ergebnis gegen NULL, initialisiere jedes Element und gib es am Ende mit free(n) frei.