webentwicklung-frage-antwort-db.com.de

Zeiger in C: Wann sind das kaufmännische Und und das Sternchen zu verwenden?

Ich fange gerade mit Zeigern an und bin etwas verwirrt. Ich weiß & bedeutet die Adresse einer Variablen und dass * kann vor einer Zeigervariable verwendet werden, um den Wert des Objekts abzurufen, auf das der Zeiger zeigt. Bei der Arbeit mit Arrays, Strings oder beim Aufrufen von Funktionen mit einer Zeigerkopie einer Variablen verhält es sich jedoch anders. Es ist schwierig, ein logisches Muster in all dem zu erkennen.

Wann sollte ich & und *?

256
Pieter

Sie haben Zeiger und Werte:

int* p; // variable p is pointer to integer type
int i; // integer value

Sie verwandeln einen Zeiger in einen Wert mit *:

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to

Sie verwandeln einen Wert in einen Zeiger mit &:

int* p2 = &i; // pointer p2 will point to the address of integer i

Bearbeiten: Bei Arrays werden sie wie Zeiger behandelt. Wenn Sie sie als Zeiger betrachten, verwenden Sie *, um zu den Werten in ihnen zu gelangen, wie oben erläutert, aber es gibt auch eine andere, gebräuchlichere Methode, die [] Operator:

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

So erhalten Sie das zweite Element:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

Also das [] Der Indexierungsoperator ist eine spezielle Form des * Operator, und es funktioniert so:

a[i] == *(a + i);  // these two statements are the same thing
560
Dan Olson

Es gibt ein Muster beim Umgang mit Arrays und Funktionen; es ist nur ein wenig schwer zu sehen.

Beachten Sie beim Umgang mit Arrays Folgendes: Wenn ein Array-Ausdruck in den meisten Kontexten angezeigt wird, wird der Typ des Ausdrucks implizit von "N-Element-Array von T" in "Zeiger auf T" konvertiert und sein Wert festgelegt um auf das erste Element im Array zu zeigen. Ausnahmen von dieser Regel sind, wenn der Array-Ausdruck als Operand der Operatoren & Oder sizeof angezeigt wird oder wenn es sich um ein Zeichenfolgenliteral handelt, das als Initialisierer in einer Deklaration verwendet wird.

Wenn Sie also eine Funktion mit einem Array-Ausdruck als Argument aufrufen, erhält die Funktion einen Zeiger und kein Array:

int arr[10];
...
foo(arr);
...

void foo(int *arr) { ... }

Aus diesem Grund nicht verwenden Sie den Operator & Für Argumente, die "% s" in scanf() entsprechen:

char str[STRING_LENGTH];
...
scanf("%s", str);

Aufgrund der impliziten Konvertierung erhält scanf() einen Wert char *, Der auf den Anfang des Arrays str verweist. Dies gilt für jede Funktion, die mit einem Array-Ausdruck als Argument aufgerufen wird (fast alle Funktionen str*, *scanf Und *printf Usw.).

In der Praxis werden Sie wahrscheinlich niemals eine Funktion mit einem Array-Ausdruck mit dem Operator & Aufrufen, wie in:

int arr[N];
...
foo(&arr);

void foo(int (*p)[N]) {...}

Ein solcher Code ist nicht sehr verbreitet; Sie müssen die Größe des Arrays in der Funktionsdeklaration kennen, und die Funktion funktioniert nur mit Zeigern auf Arrays bestimmter Größe (ein Zeiger auf ein 10-Elemente-Array von T ist ein anderer Typ als ein Zeiger auf ein 11-Elemente-Array von T).

Wenn ein Array-Ausdruck als Operand für den Operator & Angezeigt wird, lautet der Typ des resultierenden Ausdrucks "Zeiger auf das N-Element-Array von T" oder T (*)[N], was sich von einem unterscheidet Array von Zeigern (T *[N]) und ein Zeiger auf den Basistyp (T *).

Wenn Sie sich mit Funktionen und Zeigern befassen, sollten Sie sich folgende Regel merken: Wenn Sie den Wert eines Arguments ändern und ihn im aufrufenden Code wiedergeben möchten, müssen Sie einen Zeiger auf das Objekt übergeben, das Sie ändern möchten. Wieder werfen Arrays ein bisschen einen Schraubenschlüssel in die Werke, aber wir werden uns zuerst mit den normalen Fällen befassen.

Denken Sie daran, dass C alle Funktionsargumente als Wert übergibt. Der Formalparameter erhält eine Kopie des Werts im Aktualparameter. Änderungen am Formalparameter werden im Aktualparameter nicht berücksichtigt. Das übliche Beispiel ist eine Swap-Funktion:

void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);

Sie erhalten folgende Ausgabe:

 vor dem Tausch: a = 1, b = 2 
 nach dem Tausch: a = 1, b = 2 

Die formalen Parameter x und y sind verschiedene Objekte von a und b, daher werden sie in x und y geändert. werden in a und b nicht wiedergegeben. Da wir die Werte von a und b ändern möchten, müssen wir Zeiger an die Swap-Funktion übergeben:

void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);

Jetzt wird Ihre Ausgabe sein

 vor dem Tausch: a = 1, b = 2 
 nach dem Tausch: a = 2, b = 1 

Beachten Sie, dass wir in der Swap-Funktion nicht die Werte von x und y ändern, sondern die Werte von what x und y - zeigen Sie auf. Das Schreiben in *x Unterscheidet sich vom Schreiben in x; Wir aktualisieren nicht den Wert in x selbst, sondern beziehen einen Speicherort von x und aktualisieren den Wert an diesem Speicherort.

Dies gilt auch, wenn Sie einen Zeigerwert ändern möchten. wenn wir schreiben

int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);

dann ändern wir den Wert des Eingabeparameters stream, nicht den Wert von streamzeigt auf, sodass das Ändern von stream keine Auswirkungen auf den Wert hat von in; Damit dies funktioniert, müssen wir einen Zeiger an den Zeiger übergeben:

int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);

Wieder werfen Arrays ein bisschen einen Schraubenschlüssel in die Werke. Wenn Sie einen Array-Ausdruck an eine Funktion übergeben, erhält die Funktion einen Zeiger. Aufgrund der Definition der Array-Indexierung können Sie einen Indexoperator für einen Zeiger genauso verwenden wie für ein Array:

int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}

Beachten Sie, dass Array-Objekte möglicherweise nicht zugewiesen werden. Sie können so etwas nicht tun

int a[10], b[10];
...
a = b;

sie möchten also vorsichtig sein, wenn Sie mit Zeigern auf Arrays arbeiten. so etwas wie

void (int (*foo)[N])
{
  ...
  *foo = ...;
}

wird nicht funktionieren.

24
John Bode

Einfach ausgedrückt

  • & bedeutet Adresse von, Sie werden feststellen, dass in Platzhaltern für Funktionen zum Ändern der Parametervariable wie in C Parametervariablen mit dem kaufmännischen Und-Zeichen als Referenz übergeben werden.
  • * bedeutet dereferenzieren einer Zeigervariable, dh den Wert dieser Zeigervariablen abrufen.
 int foo (int * x) {
 * x ++; 
} 
 
 int main (int argc, char ** argv) {
 int y = 5; 
 foo (& y); // Jetzt wird y inkrementiert und hier im Gültigkeitsbereich 
 Printf ("Wert von y =% d\n", y); // Ausgabe ist 6 
/* ... */
} 

Das obige Beispiel zeigt, wie eine Funktion foo unter Verwendung der Referenzübergabe aufgerufen wird. Vergleichen Sie dies

 int foo (int x) {
 x ++; 
} 
 
 int main (int argc, char ** argv) {
 int y = 5; 
 foo (y); // Jetzt ist y immer noch 5 
 Printf ("Wert von y =% d\n", y); // Ausgabe ist 5 
/* ... */
} 

Hier ist ein Beispiel für die Verwendung eines Dereferenzierung

 int main (int argc, char ** argv) {
 int y = 5; 
 int * p = NULL; 
 p = & y; 
 printf ("Wert von * p =% d\n", * p); // Ausgabe ist 5 
} 

Das Obige zeigt, wie wir das address-ofy erhalten und es der Zeigervariable p zugewiesen haben. Dann werden wir dereferenzierenp durch Anhängen des * davor, um den Wert von p zu erhalten, d. h. *p.

11
t0mm13b

Ja, das kann ziemlich kompliziert sein, da * In C/C++ für viele verschiedene Zwecke verwendet wird.

Wenn * Vor einer bereits deklarierten Variablen/Funktion steht, bedeutet dies entweder:

  • a) * ermöglicht den Zugriff auf den Wert dieser Variablen (wenn der Typ dieser Variablen ein Zeigertyp ist oder der Operator * überladen wurde).
  • b) * hat die Bedeutung des Multiplikationsoperators. In diesem Fall muss links von * eine andere Variable stehen.

Wenn * In einer Variablen- oder Funktionsdeklaration erscheint, bedeutet dies, dass diese Variable ein Zeiger ist:

int int_value = 1;
int * int_ptr; //can point to another int variable
int   int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer aswell which points to the first int of the array
//int   int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5};  // these two
int int_array4[5] = {1,2,3,4,5}; // are indentical

void func_takes_int_ptr1(int *int_ptr){} // these two are indentical
void func_takes int_ptr2(int int_ptr[]){}// and legal

Wenn & In einer Variablen- oder Funktionsdeklaration erscheint, bedeutet dies im Allgemeinen, dass diese Variable auf eine Variable dieses Typs verweist.

Wenn & Vor einer bereits deklarierten Variablen steht, wird die Adresse dieser Variablen zurückgegeben

Außerdem sollten Sie wissen, dass Sie beim Übergeben eines Arrays an eine Funktion immer auch die Arraygröße dieses Arrays übergeben müssen, es sei denn, das Array ist so etwas wie ein 0-terminierter cstring (char-Array).

9
smerlin

Wenn Sie eine Zeigervariable oder einen Funktionsparameter deklarieren, verwenden Sie *:

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}

Hinweis: Jede deklarierte Variable benötigt ein eigenes *.

Wenn Sie die Adresse eines Werts übernehmen möchten, verwenden Sie &. Wenn Sie den Wert in einem Zeiger lesen oder schreiben möchten, verwenden Sie *.

int a;
int *b;
b = f(&a);
a = *b;

a = *f(&a);

Arrays werden normalerweise nur wie Zeiger behandelt. Wenn Sie einen Array-Parameter in einer Funktion deklarieren, können Sie genauso einfach festlegen, dass es sich um einen Zeiger handelt (dies bedeutet dasselbe). Wenn Sie ein Array an eine Funktion übergeben, übergeben Sie tatsächlich einen Zeiger auf das erste Element.

Funktionszeiger sind die einzigen Dinge, die den Regeln nicht ganz folgen. Sie können die Adresse einer Funktion übernehmen, ohne & zu verwenden, und Sie können einen Funktionszeiger aufrufen, ohne * zu verwenden.

4
Jay Conrod

Ich habe alle wortreichen Erklärungen durchgesehen und stattdessen ein Video von der Universität von New South Wales zur Rettung angesehen. Hier ist die einfache Erklärung: Wenn wir eine Zelle haben, die die Adresse x und den Wert 7 hat , die indirekte Möglichkeit, nach der Adresse des Werts 7 zu fragen, ist &7 und die indirekte Möglichkeit, nach dem Wert an der Adresse x zu fragen, ist *x. Also (cell: x , value: 7) == (cell: &7 , value: *x) Ein anderer Weg, es zu untersuchen: John sitzt bei 7th seat. Der *7th seat zeigt auf John und &John gibt address/Ort des 7th seat. Diese einfache Erklärung hat mir geholfen und ich hoffe, dass sie auch anderen helfen wird. Hier ist der Link für das hervorragende Video: hier klicken.

Hier ist ein weiteres Beispiel:

#include <stdio.h>

int main()
{ 
    int x;            /* A normal integer*/
    int *p;           /* A pointer to an integer ("*p" is an integer, so p
                       must be a pointer to an integer) */

    p = &x;           /* Read it, "assign the address of x to p" */
    scanf( "%d", &x );          /* Put a value in x, we could also use p here */
    printf( "%d\n", *p ); /* Note the use of the * to get the value */
    getchar();
}

Add-on: Initialisieren Sie den Zeiger immer, bevor Sie ihn verwenden. Ist dies nicht der Fall, zeigt der Zeiger auf irgendetwas, was zu einem Absturz des Programms aufgrund des Betriebssystems führen kann verhindert, dass Sie auf den Speicher zugreifen, von dem bekannt ist, dass er Ihnen nicht gehört. Durch einfaches Setzen von p = &x; weisen wir dem Zeiger eine bestimmte Position zu.

3
user6288471

Eigentlich hast du es drauf, es gibt nichts mehr, was du wissen musst :-)

Ich würde nur die folgenden Bits hinzufügen:

  • die beiden Operationen sind entgegengesetzte Enden des Spektrums. & nimmt eine Variable und gibt Ihnen die Adresse * nimmt eine Adresse und gibt Ihnen die Variable (oder den Inhalt).
  • arrays "degradieren" zu Zeigern, wenn Sie sie an Funktionen übergeben.
  • sie können tatsächlich mehrere Ebenen auf Indirektion haben (char **p bedeutet, dass p ein Zeiger auf einen Zeiger auf ein char ist.

Was die Dinge angeht, die anders funktionieren, nicht wirklich:

  • wie bereits erwähnt, werden Arrays bei der Übergabe an Funktionen zu Zeigern (zum ersten Element im Array) degradiert. Sie bewahren keine Größeninformationen auf.
  • in C gibt es keine Zeichenfolgen, sondern nur Zeichenfelder, die gemäß der Konvention eine Zeichenfolge darstellen, die durch eine Null (\0) Zeichen.
  • Wenn Sie die Adresse einer Variablen an eine Funktion übergeben, können Sie den Verweis aufheben, um die Variable selbst zu ändern (normalerweise werden Variablen als Wert übergeben (mit Ausnahme von Arrays)).
3
paxdiablo

Ich denke du bist ein bisschen verwirrt. Sie sollten ein gutes Tutorial/ein Buch über Zeiger lesen.

This Tutorial ist sehr gut für den Anfang (klar erklärt, was & und * sind). Und ja, vergessen Sie nicht, das Buch Pointer in C von Kenneth Reek zu lesen.

Der Unterschied zwischen & und * ist sehr klar.

Beispiel:

#include <stdio.h>

int main(){
  int x, *p;

  p = &x;         /* initialise pointer(take the address of x) */
  *p = 0;         /* set x to zero */
  printf("x is %d\n", x);
  printf("*p is %d\n", *p);

  *p += 1;        /* increment what p points to i.e x */
  printf("x is %d\n", x);

  (*p)++;         /* increment what p points to i.e x */
  printf("x is %d\n", x);

  return 0;
}
3
Prasoon Saurav

Ok, es sieht so aus, als ob dein Beitrag bearbeitet wurde ...

double foo[4];
double *bar_1 = &foo[0];

Sehen Sie, wie Sie die &, um die Adresse des Anfangs der Array-Struktur zu erhalten? Folgende

Foo_1(double *bar, int size){ return bar[size-1]; }
Foo_2(double bar[], int size){ return bar[size-1]; }

werde das gleiche tun.

1
wheaties