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érencez arr2
? Vous obtenez un tableau de 8 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érencez x
? Vous obtenez une fonction que vous pouvez appeler sans arguments. Que se passe-t-il lorsque vous l'appelez? Il renverra un pointeur sur a float
.
La priorité des opérateurs est cependant toujours délicate. Cependant, l'utilisation de parenthèses peut également être source de confusion car la déclaration suit l'utilisation. Au moins, pour moi, cela arr2
ressemble intuitivement à un tableau de 8 pointeurs vers des pouces, mais c'est en fait l'inverse. Il suffit de s'y habituer. Raison suffisante pour toujours ajouter un commentaire à ces déclarations, si vous me demandez :)
modifier: exemple
Soit dit en passant, je suis juste tombé sur la situation suivante: une fonction qui a une matrice statique et qui utilise l'arithmétique du pointeur pour voir si le pointeur de ligne est hors limites. Exemple:
#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;
}
Production:
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, donc le compilateur peut optimiser cela. Ceci est différent de ce que vous voudrez peut-être utiliser initialement const int (*border)[3]
:: qui déclare border comme pointeur vers un tableau de 3 entiers qui ne changera pas de valeur tant que la variable existe. Cependant, ce pointeur peut être pointé vers n'importe quel autre tableau de ce type à tout moment. Nous voulons plutôt ce type 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!)