C pointeur sur un tableau / tableau de désambiguïsation de pointeurs

Quelle est la différence entre les déclarations suivantes:

int* arr1[8]; int (*arr2)[8]; int *(arr3[8]); 

Quelle est la règle générale pour comprendre des déclarations plus complexes?

 int* arr[8]; // An array of int pointers. int (*arr)[8]; // A pointer to an array of integers 

Le troisième est le même que le premier.

La règle générale est la priorité de l’opérateur . Cela peut devenir encore plus complexe à mesure que des pointeurs de fonctions apparaissent.

Utilisez le programme cdecl , comme suggéré par K & R.

 $ 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> 

Cela fonctionne aussi dans l’autre sens.

 cdecl> declare x as pointer to function(void) returning pointer to float float *(*x)(void ) 

Je ne sais pas si elle porte un nom officiel, mais je l’appelle le Right-Left Thingy (TM).

Commencez par la variable, puis allez à droite et à gauche, et à droite … et ainsi de suite.

 int* arr1[8]; 

arr1 est un tableau de 8 pointeurs vers des entiers.

 int (*arr2)[8]; 

arr2 est un pointeur (la parenthèse bloque la droite-gauche) vers un tableau de 8 nombres entiers.

 int *(arr3[8]); 

arr3 est un tableau de 8 pointeurs vers des entiers.

Cela devrait vous aider avec des déclarations complexes.

 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 

La réponse pour les deux derniers peut également être déduite de la règle d’or en C:

La déclaration suit l’utilisation.

int (*arr2)[8];

Que se passe-t-il si vous déréférencer arr2? Vous obtenez un tableau de 8 nombres entiers.

int *(arr3[8]);

Que se passe-t-il si vous prenez un élément arr3? Vous obtenez un pointeur sur un entier.

Cela aide également lorsque vous traitez des pointeurs vers des fonctions. Pour prendre l’exemple de sigjuice:

float *(*x)(void )

Que se passe-t-il lorsque vous déréférencer x? Vous obtenez une fonction que vous pouvez appeler sans arguments. Qu’est-ce qui se passe quand vous l’appelez? Il renverra un pointeur sur un flottant.

La priorité des opérateurs est toujours délicate. Cependant, l’utilisation de parenthèses peut également être source de confusion car la déclaration suit son utilisation. Pour moi, au moins, intuitivement arr2 ressemble à un tableau de 8 pointeurs vers ints, mais c’est en réalité l’inverse. Il faut juste s’y habituer. Raison suffisante pour toujours append un commentaire à ces déclarations, si vous me le demandez 🙂

edit: exemple

Au fait, je suis juste tombé sur la situation suivante: une fonction qui a une masortingce statique et qui utilise l’arithmétique de pointeur pour voir si le pointeur de ligne est hors limites. Exemple:

 #include  #include  #include  #define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0])) int * put_off(const int newrow[2]) { static int mymasortingx[3][2]; static int (*rowp)[2] = mymasortingx; int (* const border)[] = mymasortingx + NUM_ELEM(mymasortingx); memcpy(rowp, newrow, sizeof(*rowp)); rowp += 1; if (rowp == border) { rowp = mymasortingx; } 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; } 

Sortie:

  0 (0x804a02c): [0, 0]
 1 (0x804a034): [0, 0]
 2 (0x804a024): [0, 1]
 3 (0x804a02c): [1, 2]
 4 (0x804a034): [2, 4]
 5 (0x804a024): [3, 7] 

Notez que la valeur de border ne change jamais, le compilateur peut donc l'optimiser. Ceci est différent de ce que vous pourriez vouloir initialement utiliser: const int (*border)[3] : qui déclare border comme un pointeur sur un tableau de 3 entiers qui ne changeront pas de valeur tant que la variable existe. Cependant, ce pointeur peut être dirigé vers tout autre tableau de ce type à tout moment. Nous voulons plutôt ce genre de comportement pour l'argument (car cette fonction ne modifie aucun de ces entiers). La déclaration suit l'utilisation.

(ps: n'hésitez pas à améliorer cet échantillon!)

 typedef int (*PointerToIntArray)[]; typedef int *ArrayOfIntPointers[]; 

Je pense que nous pouvons utiliser la règle simple ..

 example int * (*ptr)()[]; start from ptr 

ptr est un pointeur pour” aller vers la droite ..its “)” maintenant, allez à gauche a “(” sortez, allez bien “()” so “pour une fonction qui ne prend aucun argument” go left “et retourne un pointeur” allez droit “à un tableau” allez à gauche “des entiers”

En règle générale, les opérateurs unaires (tels que [] , () , etc.) ont la préférence sur ceux de gauche. Donc, int *(*ptr)()[]; serait un pointeur qui pointe vers une fonction qui renvoie un tableau de pointeurs vers int (obtenez les bons opérateurs dès que possible en sortant de la parenthèse)

Voici comment je l’interprète:

 int *something[n]; 

Remarque sur la priorité: l’opérateur opérateur indice (‘[]’) a une priorité plus élevée que l’opérateur de déréférence (‘*’).

Donc, ici, nous appliquerons le ‘[]’ avant ‘*’, rendant la déclaration équivalente à:

 int *(something[i]); 

notez comment une déclaration a du sens: int num (num) est un (int), int *ptr ou int (*ptr) signifie, (value at ptr) est un (int), ce qui fait de ptr un pointeur sur int.

Cela peut être lu comme, (la valeur de la (valeur à l’index de l’index de quelque chose)) est un entier. Donc, (valeur à l’index de quelque chose) est un (pointeur entier), ce qui fait que quelque chose est un tableau de pointeurs entiers.

Dans le second,

 int (*something)[n]; 

Pour bien comprendre cette affirmation, vous devez connaître ce fait:

note sur la représentation du pointeur du tableau: quelque choseElse [i] équivaut à * (quelque chose + i)

Donc, en remplaçant quelque chose par “(quelque chose)”, on obtient * (* quelque chose + i), qui est un entier selon la déclaration. Donc, (* quelque chose) nous a donné un tableau, ce qui fait que quelque chose est équivalent à (pointeur vers un tableau).

Je suppose que la deuxième déclaration porte à confusion. Voici un moyen facile de le comprendre.

Avons un tableau d’entiers, ie int B[8] .

Avons aussi une variable A qui pointe vers B. Maintenant, la valeur à A est B, c’est-à-dire (*A) == B D’où A pointe vers un tableau d’entiers. Dans votre question, arr est similaire à A.

De même, dans int* (*C) [8] , C est un pointeur sur un tableau de pointeurs en entier.

Voici un site intéressant qui explique comment lire les types complexes dans C: http://www.unixwiz.net/techtips/reading-cdecl.html

Dans un pointeur sur un entier si le pointeur est incrémenté, il passe ensuite au nombre entier suivant.

dans le tableau de pointeur si le pointeur est incrémenté, il passe au tableau suivant