Was ist der Unterschied zwischen den folgenden Erklärungen:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Was ist die allgemeine Regel für das Verständnis komplexerer Erklärungen?
quelle
Was ist der Unterschied zwischen den folgenden Erklärungen:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Was ist die allgemeine Regel für das Verständnis komplexerer Erklärungen?
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
Der dritte ist der gleiche wie der erste.
Die allgemeine Regel ist die Priorität des Operators . Es kann noch viel komplexer werden, wenn Funktionszeiger ins Bild kommen.
( ) [ ]
assoziieren von links nach rechts und haben eine höhere Priorität als *
das, was int* arr[8]
als Array der Größe 8 gelesen wird, wobei jedes Element auf ein int zeigt, und int (*arr)[8]
als Zeiger auf ein Array der Größe 8, das ganze Zahlen enthält
Verwenden Sie das von K & R vorgeschlagene Programm cdecl .
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
Es funktioniert auch umgekehrt.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
Ich weiß nicht, ob es einen offiziellen Namen hat, aber ich nenne es das Right-Left Thingy (TM).
Beginnen Sie bei der Variablen, gehen Sie dann nach rechts und links und rechts ... und so weiter.
int* arr1[8];
arr1
ist ein Array von 8 Zeigern auf Ganzzahlen.
int (*arr2)[8];
arr2
ist ein Zeiger (der Klammerblock rechts-links) auf ein Array von 8 Ganzzahlen.
int *(arr3[8]);
arr3
ist ein Array von 8 Zeigern auf Ganzzahlen.
Dies sollte Ihnen bei komplexen Deklarationen helfen.
int *a[][10]
während letztere gelingt.
( ) [ ]
und von rechts nach links von* &
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
[5]
) repräsentiert die innere Dimension. Dies bedeutet, dass dies (*a[8])
die erste Dimension und somit die äußere Darstellung des Arrays ist. Was jedes Element innerhalb a
zeigt, ist ein anderes ganzzahliges Array der Größe 5.
Die Antwort für die letzten beiden kann auch von der goldenen Regel in C abgezogen werden:
Erklärung folgt Verwendung.
int (*arr2)[8];
Was passiert, wenn Sie dereferenzieren arr2
? Sie erhalten ein Array von 8 Ganzzahlen.
int *(arr3[8]);
Was passiert, wenn Sie ein Element entnehmen arr3
? Sie erhalten einen Zeiger auf eine Ganzzahl.
Dies hilft auch beim Umgang mit Zeigern auf Funktionen. Um das Beispiel von Sigjuice zu nehmen:
float *(*x)(void )
Was passiert, wenn Sie dereferenzieren x
? Sie erhalten eine Funktion, die Sie ohne Argumente aufrufen können. Was passiert, wenn Sie es nennen? Es wird ein Zeiger auf a zurückgegeben float
.
Die Priorität des Bedieners ist jedoch immer schwierig. Die Verwendung von Klammern kann jedoch auch verwirrend sein, da die Deklaration auf die Verwendung folgt. Zumindest arr2
sieht es für mich intuitiv aus wie ein Array von 8 Zeigern auf Ints, aber es ist tatsächlich umgekehrt. Es ist nur gewöhnungsbedürftig. Grund genug, diesen Erklärungen immer einen Kommentar hinzuzufügen, wenn Sie mich fragen :)
bearbeiten: Beispiel
Übrigens bin ich gerade auf die folgende Situation gestoßen: Eine Funktion mit einer statischen Matrix, die Zeigerarithmetik verwendet, um festzustellen, ob der Zeilenzeiger außerhalb der Grenzen liegt. Beispiel:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
Ausgabe:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Beachten Sie, dass sich der Wert von border nie ändert, sodass der Compiler dies optimieren kann. Dies unterscheidet sich von dem, was Sie ursprünglich verwenden möchten const int (*border)[3]
:: Das deklariert border als Zeiger auf ein Array von 3 Ganzzahlen, die den Wert nicht ändern, solange die Variable vorhanden ist. Dieser Zeiger kann jedoch jederzeit auf ein anderes solches Array gerichtet sein. Wir wollen stattdessen diese Art von Verhalten für das Argument (weil diese Funktion keine dieser ganzen Zahlen ändert). Erklärung folgt Verwendung.
(ps: zögern Sie nicht, dieses Beispiel zu verbessern!)
Als Faustregel gilt : rechts unäre Operatoren (wie []
, ()
usw.) haben Vorrang vor links diejenigen. Wäre int *(*ptr)()[];
also ein Zeiger, der auf eine Funktion verweist, die ein Array von Zeigern auf int zurückgibt (holen Sie sich die richtigen Operatoren, sobald Sie aus der Klammer herauskommen können).
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
unter GCC 8 mit$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
ermöglicht die spätere Verwendung eines Ausdrucks wie p()[3]
(oder (*p)()[3]
).
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
und nenne es so: foo(arr)[4];
was soll enthalten arr[2][4]
, oder?
Ich denke, wir können die einfache Regel anwenden.
example int * (*ptr)()[];
start from ptr
" ptr
ist ein Zeiger auf" nach rechts gehen .. es ")" jetzt nach links gehen ist ein "(" herauskommen nach rechts gehen "()" also "auf eine Funktion, die keine Argumente akzeptiert" nach links gehen "und einen Zeiger zurückgeben" gehen " rechts "zu einem Array" gehe links "von ganzen Zahlen"
)
, jetzt nach links gehen ... es ist *
"ein Zeiger auf" nach rechts gehen ... es ist )
, jetzt nach links gehen ... es ist a (
komm raus, gehe nach rechts ()
so "zu einer Funktion, die keine Argumente akzeptiert" gehe nach rechts ... []
"und gebe ein Array von" nach rechts gehen ;
, also nach links gehen ... *
"Zeiger auf" nach links gehen ... int
"Ganzzahlen" zurück.
Hier ist eine interessante Website, auf der erklärt wird, wie komplexe Typen in C gelesen werden: http://www.unixwiz.net/techtips/reading-cdecl.html
So interpretiere ich es:
int *something[n];
Hinweis zur Priorität: Der Array-Indexoperator (
[]
) hat eine höhere Priorität als der Dereferenzierungsoperator (*
).
Also, hier werden wir die Anwendung []
vor *
, um die Anweisung äquivalent zu machen:
int *(something[i]);
Hinweis darüber , wie eine Erklärung macht Sinn:
int num
Mittelnum
einint
,int *ptr
oderint (*ptr)
Mittel, (Wert beiptr
) einint
, wodurchptr
auf einen Zeigerint
.
Dies kann gelesen werden als (Wert von (Wert am i-ten Index des Etwas)) ist eine ganze Zahl. Also (Wert am i-ten Index von etwas) ist ein (ganzzahliger Zeiger), der das Etwas zu einem Array von ganzzahligen Zeigern macht.
Im zweiten,
int (*something)[n];
Um aus dieser Aussage einen Sinn zu machen, müssen Sie mit dieser Tatsache vertraut sein:
Hinweis zur Zeigerdarstellung des Arrays:
somethingElse[i]
entspricht*(somethingElse + i)
Also, ersetzt somethingElse
mit (*something)
, bekommen wir *(*something + i)
, was eine ganze Zahl als pro - Deklaration. Geben Sie (*something)
uns also ein Array, das etwas Äquivalentes zu (Zeiger auf ein Array) macht .
Ich denke, die zweite Erklärung ist für viele verwirrend. Hier ist eine einfache Möglichkeit, dies zu verstehen.
Lassen Sie uns ein Array von ganzen Zahlen haben, dh int B[8]
.
Lassen Sie uns auch eine Variable A haben, die auf B zeigt. Nun ist der Wert bei A B, dh (*A) == B
. Daher zeigt A auf ein Array von ganzen Zahlen. In Ihrer Frage ähnelt arr A.
In ähnlicher Weise ist in int* (*C) [8]
C ein Zeiger auf ein Array von Zeigern auf eine Ganzzahl.
int *arr1[5]
In dieser Deklaration arr1
befindet sich ein Array von 5 Zeigern auf Ganzzahlen. Grund: Eckige Klammern haben eine höhere Priorität als * (Dereferenzierungsoperator). Bei diesem Typ ist die Anzahl der Zeilen festgelegt (hier 5), die Anzahl der Spalten ist jedoch variabel.
int (*arr2)[5]
In dieser Deklaration arr2
ist ein Zeiger auf ein ganzzahliges Array von 5 Elementen. Grund: Hier haben () Klammern eine höhere Priorität als []. Bei diesem Typ ist die Anzahl der Zeilen variabel, aber die Anzahl der Spalten ist fest (hier 5).
Wenn der Zeiger im Zeiger auf eine Ganzzahl inkrementiert wird, wird die nächste Ganzzahl verwendet.
Wenn im Zeigerarray der Zeiger inkrementiert ist, springt er zum nächsten Array
const
undvolatile
Qualifikation , die wichtig und heikel ist, wird in diesem Artikel fehlt.