Was ist der Unterschied zwischen den folgenden Deklarationen:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Was ist die allgemeine Regel zum Verständnis komplexerer Deklarationen?
George
Was ist der Unterschied zwischen den folgenden Deklarationen:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Was ist die allgemeine Regel zum Verständnis komplexerer Deklarationen?
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
Der dritte ist derselbe wie der erste.
Die allgemeine Regel ist Operator Vorrang. Es kann noch viel komplexer werden, wenn Funktionszeiger ins Spiel kommen.
Also für 32-Bit-Systeme: int* arr[8]; /* 8×4 Bytes zugewiesen, für jeden Zeiger /int (*arr)[8]; / 4 Bytes belegt, nur ein Zeiger */
– George
13. Mai 2009 um 18:43 Uhr
Nö. int* anf[8]: 8×4 Bytes zugewiesen gesamt, 4 Bytes für jeden Zeiger. int (*arr)[8] stimmt, 4 Bytes.
– mmx
13. Mai 2009 um 18:49 Uhr
Ich hätte nochmal lesen sollen, was ich geschrieben habe. Ich meinte 4 für jeden Zeiger. Danke für die Hilfe!
– George
13. Mai 2009 um 18:59 Uhr
Der Grund dafür, dass der erste derselbe ist wie der letzte, ist, dass es immer erlaubt ist, Deklaratoren in Klammern zu setzen. P[N] ist ein Array-Deklarator. P(….) ist ein Funktionsdeklarator und *P ist ein Zeigerdeklarator. Im Folgenden ist also alles so wie ohne Klammern (bis auf das “()” der Funktionen): int (((*p))); void ((g(void))); int *(a[1]); ungültig (*(p())).
– Johannes Schaub – litb
13. Mai 2009 um 20:21 Uhr
Gut gemacht in deiner Erklärung. Eine ausführliche Referenz zu Vorrang und Assoziativität von Operatoren finden Sie auf Seite 53 von The C Programming Language (ANSI C zweite Ausgabe) von Brian Kernighan und Dennis Ritchie. Die Betreiber ( ) [ ]
assoziieren von links nach rechts und haben eine höhere Priorität als *
also lesen int* arr[8]
als Array der Größe 8, wobei jedes Element auf ein int und zeigt int (*arr)[8]
als Zeiger auf ein Array der Größe 8, das ganze Zahlen enthält
– Matschig
23. September 2017 um 14:03 Uhr
Sigisaft
Verwenden Sie die cdekl Programm, wie von K&R vorgeschlagen.
$ 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 andersherum.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
@ankii Die meisten Linux-Distributionen sollten ein Paket haben. Sie könnten auch Ihre eigene Binärdatei erstellen.
– Sigissaft
15. Oktober 2019 um 17:05 Uhr
ah sorry, dass ich es nicht erwähnt habe, macOS hier. Mal sehen, ob verfügbar, sonst ist die Website auch in Ordnung. ^^ Danke, dass du mich darüber informiert hast. Fühle dich frei, NLN zu melden.
– Benutzer10063119
15. Oktober 2019 um 17:06 Uhr
@ankii Sie können von Homebrew (und vielleicht MacPorts?) Installieren. Wenn diese nicht nach Ihrem Geschmack sind, ist es trivial, Ihre eigenen über den Github-Link oben rechts auf cdecl.org zu erstellen (ich habe ihn gerade auf macOS Mojave erstellt). Kopieren Sie dann einfach die cdecl-Binärdatei in Ihren PATH. Ich empfehle $PATH/bin, weil es nicht nötig ist, root in etwas so einfaches wie dieses einzubeziehen.
– Sigissaft
15. Oktober 2019 um 17:18 Uhr
Oh hatte den kleinen Absatz über die Installation in der Readme-Datei nicht gelesen. nur einige Befehle und Flags für den Umgang mit Abhängigkeiten. Installiert mit brew. 🙂
– Benutzer10063119
15. Oktober 2019 um 17:35 Uhr
GManNickG
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 ganze Zahlen.
int (*arr2)[8];
arr2
ist ein Zeiger (der Klammerblock rechts-links) auf ein Array von 8 ganzen Zahlen.
int *(arr3[8]);
arr3
ist ein Array von 8 Zeigern auf ganze Zahlen.
Dies sollte Ihnen bei komplexen Deklarationen helfen.
Ich habe gehört, dass es unter dem Namen “The Spiral Rule” bezeichnet wird, der gefunden werden kann hier.
– fourisch
27. Mai 2013 um 3:04 Uhr
@InkBlend: Die Spiralregel unterscheidet sich von Rechts-Links-Regel. Ersteres versagt in Fällen wie int *a[][10]
während letzteres gelingt.
– legends2k
9. Oktober 2013 um 13:51 Uhr
Wie InkBlend und legends2k sagten, ist dies eine Spiralregel, die komplexer ist und nicht in allen Fällen funktioniert, daher gibt es keinen Grund, sie zu verwenden.
– kotlomoi
25. September 2014 um 18:29 Uhr
Vergessen Sie nicht die Assoziativität von links nach rechts ( ) [ ]
und von rechts nach links * &
– Matschig
23. September 2017 um 14:10 Uhr
@legends2k: Was ist die Deklarationsanzeige für int *a[][10]
– Bestienjunge
21. November 2018 um 4:37 Uhr
Sunil Mrd
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
Die Antwort für die letzten beiden lässt sich auch aus der goldenen Regel in C ableiten:
Die Deklaration folgt der Verwendung.
int (*arr2)[8];
Was passiert, wenn Sie dereferenzieren arr2
? Sie erhalten ein Array von 8 ganzen Zahlen.
int *(arr3[8]);
Was passiert, wenn Sie ein Element aus nehmen arr3
? Sie erhalten einen Zeiger auf eine ganze Zahl.
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 anrufen? Es wird einen Zeiger auf a zurückgeben float
.
Der Vorrang von Operatoren ist jedoch immer schwierig. Die Verwendung von Klammern kann jedoch auch verwirrend sein, da die Deklaration auf die Verwendung folgt. Zumindest für mich intuitiv arr2
sieht aus wie ein Array von 8 Zeigern auf ints, aber es ist tatsächlich umgekehrt. Nur etwas gewöhnungsbedürftig. Grund genug, diese Erklärungen immer zu kommentieren, wenn Sie mich fragen 🙂
edit: beispiel
Übrigens bin ich gerade über die folgende Situation gestolpert: eine Funktion, die eine statische Matrix hat und die Zeigerarithmetik verwendet, um zu sehen, 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 den Rand als Zeiger auf ein Array von 3 Ganzzahlen, die den Wert nicht ändern, solange die Variable existiert. Dieser Zeiger kann jedoch jederzeit auf ein beliebiges anderes solches Array zeigen. Wir wollen stattdessen diese Art von Verhalten für das Argument (weil diese Funktion keine dieser ganzen Zahlen ändert). Die Deklaration folgt der Verwendung.
(ps: Fühlen Sie sich frei, dieses Beispiel zu verbessern!)
Byron Formwalt
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
Luis Colorado
Als Faustregel gilt, dass rechte unäre Operatoren (wie []
, ()
, etc) haben Vorrang vor linken. So, int *(*ptr)()[];
wäre ein Zeiger, der auf eine Funktion zeigt, die ein Array von Zeigern auf int zurückgibt (holen Sie sich die richtigen Operatoren, sobald Sie aus der Klammer kommen)
Das stimmt, ist aber auch illegal. Sie können keine Funktion haben, die ein Array zurückgibt. Ich habe versucht und das bekommen: 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
– alx
24. Februar 2019 um 1:48 Uhr
Der Compiler gibt diesen Fehler aus, weil er die Funktion als Rückgabe eines Arrays interpretiert, wie es die korrekte Interpretation der Vorrangregel besagt. Es ist als Erklärung rechtswidrig, aber die rechtliche Erklärung int *(*ptr)();
erlaubt einen Ausdruck wie p()[3]
(oder (*p)()[3]
) später verwendet werden.
– Luis Colorado
25. Februar 2019 um 19:39 Uhr
Ok, wenn ich es verstehe, sprechen Sie davon, eine Funktion zu erstellen, die einen Zeiger auf das erste Element eines Arrays (nicht ein Array selbst) zurückgibt, und diese Funktion später so zu verwenden, als ob sie ein Array zurückgeben würde? Interessante Idee. Ich werde es versuchen. int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
und nenne es so: foo(arr)[4];
was enthalten soll arr[2][4]
Rechts?
– alx
26. Februar 2019 um 11:35 Uhr
richtig… aber du hattest auch recht, und die Deklaration war rechtswidrig. 🙂
– Luis Colorado
27. Februar 2019 um 4:53 Uhr
Hier ist ein großartiger Artikel über das Lesen komplexer Deklarationen in C: unixwiz.net/techtips/reading-cdecl.html
– Jesper
27. Januar 2013 um 13:00 Uhr
@jesper Leider ist das
const
undvolatile
Qualifikationsmerkmale, die sowohl wichtig als auch knifflig sind, fehlen in diesem Artikel.– kein Benutzer
18. Mai 2017 um 15:32 Uhr