Wie versteht man den Zeigerstern * in C?

Lesezeit: 7 Minuten

Ich kämpfe mit dem Zeigerzeichen *, ich finde es sehr verwirrend, wie es sowohl in Deklarationen als auch in Ausdrücken verwendet wird.

Zum Beispiel:

int *i; // i is a pointer to an int

Aber was ist die Logik hinter der Syntax? Was bedeutet das * vor dem i? Nehmen wir das folgende Beispiel. Bitte korrigiert mich wo ich falsch liege:

char **s;
char *(*s); // added parentheses to highlight precedence

Und hier verliere ich den Überblick. Das *s zwischen den Klammern bedeutet: s ist ein Zeiger? Aber ein Hinweis auf was? Und was bedeutet das * außerhalb der Klammern: ein Zeiger auf das, worauf s zeigt?

Die Bedeutung davon ist also: Der Zeiger, der auf das zeigt, worauf s zeigt, ist ein Zeiger auf ein Zeichen?

Ich bin ratlos. Wird das *-Zeichen in Deklarationen und Ausdrücken unterschiedlich interpretiert? Wenn ja, wie wird es anders interpretiert? Wo gehe ich falsch?

Nimm es so:

int *i bedeutet, dass der Wert, auf den i zeigt, eine ganze Zahl ist.

char **p bedeutet, dass p ein Zeiger ist, der selbst ein Zeiger auf ein Zeichen ist.Geben Sie hier die Bildbeschreibung ein

Benutzeravatar von nos
Nr

int i; //i is an int.
int *i; //i is a pointer to an int
int **i;//i is a pointer to a pointer to an int.

Wird das *-Zeichen in Deklarationen und Ausdrücken unterschiedlich interpretiert?

Ja. Sie sind völlig anders. in einer Deklaration * wird verwendet, um Zeiger zu deklarieren. In einem Ausdruck wird unär * verwendet, um einen Zeiger zu dereferenzieren (oder als binärer Multiplikationsoperator).

Einige Beispiele:

int i = 10; //i is an int, it has allocated storage to store an int.
int *k; // k is an uninitialized pointer to an int. 
        //It does not store an int, but a pointer to one.
k = &i; // make k point to i. We take the address of i and store it in k
int j = *k; //here we dereference the k pointer to get at the int value it points
            //to. As it points to i, *k will get the value 10 and store it in j

Benutzeravatar von Burningice
brennendes Eis

Die Deklarationsregel in c lautet, Sie deklarieren es so, wie Sie es verwenden.

char *p bedeutet, Sie brauchen *p um den Char zu bekommen,

char **p bedeutet, Sie brauchen **p um das Zeichen zu bekommen.

Deklarationen in C sind ausdruckszentriert, was bedeutet, dass die Form der Deklaration mit der Form des Ausdrucks im ausführbaren Code übereinstimmen sollte.

Angenommen, wir haben einen Zeiger auf eine ganze Zahl mit dem Namen p. Wir wollen auf den Integer-Wert zugreifen, auf den gezeigt wird palso wir Dereferenzierung der Zeiger, etwa so:

x = *p; 

Die Art der Ausdruck *p ist int; daher die Erklärung von p nimmt die Gestalt an

int *p;

In dieser Erklärung int ist der Typbezeichnerund *p ist der Deklarator. Der Deklarator führt den Namen des deklarierten Objekts ein (p), zusammen mit zusätzlichen Typinformationen, die nicht vom Typbezeichner bereitgestellt werden. In diesem Fall ist die zusätzliche Typinformation that p ist ein Zeigertyp. Die Erklärung kann entweder gelesen werden als “p ist vom Typ Zeiger auf int” oder “p ist ein Zeiger auf Typ int“. Ich verwende lieber die zweite Form, andere bevorzugen die erste.

Es ist ein Zufall der C- und C++-Syntax, dass Sie diese Deklaration als beides schreiben können int *p; oder int* p;. In beiden Fällen wird es analysiert als int (*p); – mit anderen Worten, die * ist immer dem Variablennamen zugeordnet, nicht dem Typbezeichner.

Nehmen wir nun an, wir haben ein Array von Zeigern auf int, und wir wollen auf den Wert zugreifen, auf den das i-te Element des Arrays zeigt. Wir subskriptieren das Array und dereferenzieren das Ergebnis wie folgt:

x = *ap[i]; // parsed as *(ap[i]), since subscript has higher precedence
            // than dereference.

Auch hier die Art der Ausdruck *ap[i] ist intso die Deklaration von ap ist

int *ap[N];

wo der Deklarator *ap[N] bedeutet das ap ist ein Array von Zeigern auf int.

Und nur um den Punkt klarzumachen, nehmen wir jetzt an, wir haben einen Zeiger auf einen Zeiger auf int und auf diesen Wert zugreifen möchten. Wieder dereferenzieren wir den Zeiger, dann dereferenzieren wir dieses Ergebnis, um den ganzzahligen Wert zu erhalten:

x = **pp; // *pp deferences pp, then **pp dereferences the result of *pp

Da der Typ des Ausdrucks **pp ist intlautet die Deklaration

int **pp;

Der Deklarator **pp zeigt an, dass pp ist ein Zeiger auf einen anderen Zeiger auf ein int.

Doppelte Indirektion tritt häufig auf, typischerweise wenn Sie einen Zeigerwert ändern möchten, den Sie an eine Funktion übergeben, wie zum Beispiel:

void openAndInit(FILE **p)
{
  *p = fopen("AFile.txt", "r");
  // do other stuff
}

int main(void)
{
  FILE *f = NULL;
  ...
  openAndInit(&f);
  ...
}

In diesem Fall möchten wir, dass die Funktion den Wert von aktualisiert f; Dazu müssen wir einen Zeiger auf übergeben f. Seit f ist bereits ein Zeigertyp (FILE *), das heißt, wir übergeben einen Zeiger auf a FILE *daher die Deklaration von p wie FILE **p. Denken Sie daran, dass die Ausdruck *p in openAndInit bezieht sich auf dasselbe Objekt wie der Ausdruck f in main tut.

Sowohl in Deklarationen als auch in Ausdrücken, both [] und () haben eine höhere Priorität als unäre *. Zum Beispiel, *ap[i] wird interpretiert als *(ap[i]); der Ausdruck ap[i] ist ein Zeigertyp, und die * dereferenziert diesen Zeiger. Daher ap ist ein Array von Zeigern. Wenn Sie a Zeiger auf ein Arraymüssen Sie die explizit gruppieren * mit dem Array-Namen, etwa so:

int (*pa)[N]; // pa is a pointer to an N-element array of int

und wenn Sie auf einen Wert im Array zugreifen möchten, müssen Sie referenzieren pa vor dem Anwenden des Index:

x = (*pa)[i];

Ähnlich bei Funktionen:

int *f(); // f is a function that returns a pointer to int
...
x = *f(); // we must dereference the result of f() to get the int value

int (*f)(); // f is a pointer to a function that returns an int
...
x = (*f)(); // we must dereference f and execute the result to get the int value

Meine Lieblingsmethode zum Analysieren komplizierter Deklaratoren ist die Spiralregel im Uhrzeigersinn.

Grundsätzlich beginnst du bei der Kennung und folgst einer Spirale im Uhrzeigersinn. Sehen Sie sich den Link an, um genau zu erfahren, wie es verwendet wird.

Zwei Dinge, die der Artikel nicht erwähnt:

1- Sie sollten den Typbezeichner (int, char usw.) vom Deklarator trennen, den Deklarator analysieren und dann den Typbezeichner hinzufügen.

2- Wenn Sie auf eckige Klammern stoßen, die ein Array bezeichnen, stellen Sie sicher, dass Sie auch die folgenden eckigen Klammern (falls vorhanden) lesen.

  • Können Sie Beispiele für Ihre 1 und 2 geben? mit auslesen? wäre eine schöne ergänzung

    – n611x007

    12. September 2015 um 20:36 Uhr


  • Beachten Sie, dass Sie, wenn Sie diese Regel “im Uhrzeigersinn” nennen möchten, auch daran denken müssen, vom Namen nach oben zu gehen, da Sie sonst falsche Ergebnisse erhalten.

    – Mikko Rantalainen

    28. Februar 2018 um 10:05 Uhr

Benutzeravatar von Armen Tsirunyan
Armen Tsirunjan

int * i bedeutet, dass i ein Zeiger auf int ist (rückwärts lesen, * als Zeiger lesen).
char **p und char *(*p) beide bedeuten einen Zeiger auf einen Zeiger auf char.

Hier sind einige andere Beispiele

int* a[3] // a ist ein Array von 3 Zeigern auf int

int (*a)[3] //a ist ein Zeiger auf ein Array von 3 Ints

  • Können Sie Beispiele für Ihre 1 und 2 geben? mit auslesen? wäre eine schöne ergänzung

    – n611x007

    12. September 2015 um 20:36 Uhr


  • Beachten Sie, dass Sie, wenn Sie diese Regel “im Uhrzeigersinn” nennen möchten, auch daran denken müssen, vom Namen nach oben zu gehen, da Sie sonst falsche Ergebnisse erhalten.

    – Mikko Rantalainen

    28. Februar 2018 um 10:05 Uhr

Shamim Hafiz – Benutzeravatar von MSFT
Shamim Hafiz – MSFT

Sie haben die Antwort in Ihren Fragen.

Tatsächlich wird ein Doppelstern verwendet, um Zeiger auf Zeiger anzuzeigen.

1408200cookie-checkWie versteht man den Zeigerstern * in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy