Es gibt ein Muster im Umgang mit Arrays und Funktionen; es ist nur anfangs etwas schwer zu erkennen.
Beim Umgang mit Arrays ist es nützlich, sich an Folgendes zu erinnern: Wenn ein Array-Ausdruck in den meisten Kontexten auftaucht, wird der Typ des Ausdrucks implizit von "N-Element-Array von T" in "Zeiger auf T" umgewandelt, und sein Wert wird so gesetzt, dass er auf das erste Element im Array zeigt. Die Ausnahmen von dieser Regel sind, wenn der Array-Ausdruck als Operand von entweder der &
ou sizeof
Operatoren oder wenn es sich um ein Zeichenkettenliteral 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 nicht ein Array:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
Aus diesem Grund müssen Sie nicht verwenden Sie die &
Operator für Argumente, die "%s" entsprechen, in scanf()
:
char str[STRING_LENGTH];
...
scanf("%s", str);
Wegen der impliziten Umwandlung, scanf()
erhält eine char *
Wert, der auf den Anfang der Datei str
Array. Dies gilt für jede Funktion, die mit einem Array-Ausdruck als Argument aufgerufen wird (so gut wie jede der str*
Funktionen, *scanf
y *printf
Funktionen, usw.).
In der Praxis werden Sie wahrscheinlich nie eine Funktion mit einem Array-Ausdruck aufrufen, indem Sie die &
Operator, wie in:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
Solcher Code ist nicht sehr gebräuchlich; Sie müssen die Größe des Arrays in der Funktionsdeklaration kennen, und die Funktion funktioniert nur mit Zeigern auf Arrays bestimmter Größen (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 die Funktion &
Operator ist der Typ des resultierenden Ausdrucks "Zeiger auf N-Element-Array von T", oder T (*)[N]
was sich von einem Array von Zeigern ( T *[N]
) und einen Zeiger auf den Basistyp ( T *
).
Im Umgang mit Funktionen und Zeigern gilt die folgende Regel: Wenn Sie den Wert eines Arguments ändern wollen und dies im aufrufenden Code widergespiegelt werden soll, müssen Sie einen Zeiger auf das zu ändernde Objekt übergeben. Auch hier machen Arrays die Sache etwas komplizierter, aber wir werden uns zuerst mit den normalen Fällen beschäftigen.
Denken Sie daran, dass C die todos Funktionsargumente nach Wert; der formale Parameter erhält eine Kopie des Wertes im aktuellen Parameter, und alle Änderungen am formalen Parameter werden nicht im aktuellen Parameter berücksichtigt. Das gängige 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 werden die folgende Ausgabe erhalten:
before swap: a = 1, b = 2
after swap: a = 1, b = 2
Die formalen Parameter x
y y
sind unterschiedliche Objekte von a
y b
, also Änderungen an x
y y
werden nicht reflektiert in a
y b
. Da wir die Werte von a
y b
müssen wir passieren Zeiger zu ihnen in die 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);
Ihre Ausgabe wird nun sein
before swap: a = 1, b = 2
after swap: a = 2, b = 1
Beachten Sie, dass wir in der Swap-Funktion nicht die Werte von x
y y
sondern die Werte dessen, was x
y y
zeigen auf . Schreiben an *x
ist anders als das Schreiben an x
; wir aktualisieren nicht den Wert in x
selbst, erhalten wir einen Standort von x
und aktualisieren Sie den Wert an dieser Stelle.
Dies gilt auch, wenn wir einen Zeigerwert ändern wollen; 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 das, was stream
zeigt auf so ändern stream
hat keinen Einfluss auf den Wert von in
Damit dies funktioniert, müssen wir einen Zeiger auf den Zeiger übergeben:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
Auch hier bringen Arrays ein wenig Unruhe ins Spiel. Wenn Sie einen Array-Ausdruck an eine Funktion übergeben, erhält die Funktion einen Zeiger. Aufgrund der Definition von Array Subscripting können Sie einen Subscript-Operator für einen Zeiger auf die gleiche Weise 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 nicht zugewiesen werden dürfen, d.h. Sie können nicht etwas tun wie
int a[10], b[10];
...
a = b;
Sie sollten also vorsichtig sein, wenn Sie mit Zeigern auf Arrays arbeiten; etwas wie
void (int (*foo)[N])
{
...
*foo = ...;
}
wird nicht funktionieren.