La risposta per gli ultimi due può anche essere dedotta dalla regola d'oro in C:
La dichiarazione segue l'uso.
int (*arr2)[8];
Cosa succede se fai la dereferenza arr2
? Ottieni una matrice di 8 numeri interi.
int *(arr3[8]);
Cosa succede se prendi un elemento da arr3
? Ottieni un puntatore a un numero intero.
Questo aiuta anche quando si tratta di puntatori a funzioni. Per prendere l'esempio di sigjuice:
float *(*x)(void )
Cosa succede quando si dereference x
? Ottieni una funzione che puoi chiamare senza argomenti. Cosa succede quando lo chiami? Restituirà un puntatore a float
.
La precedenza dell'operatore è sempre difficile, però. Tuttavia, l'uso delle parentesi può anche essere fonte di confusione perché la dichiarazione segue l'uso. Almeno, per me, arr2
sembra intuitivamente come un array di 8 puntatori a inte, ma in realtà è il contrario. Ci vuole solo un po 'per abituarsi. Motivo sufficiente per aggiungere sempre un commento a queste dichiarazioni, se me lo chiedi :)
modifica: esempio
A proposito, mi sono appena imbattuto nella seguente situazione: una funzione che ha una matrice statica e che utilizza l'aritmetica del puntatore per vedere se il puntatore di riga è fuori limite. Esempio:
#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;
}
Produzione:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Si noti che il valore di border non cambia mai, quindi il compilatore può ottimizzarlo via. Questo è diverso da quello che potresti voler usare inizialmente const int (*border)[3]
:: che dichiara il bordo come un puntatore a un array di 3 numeri interi che non cambieranno valore finché esiste la variabile. Tuttavia, tale puntatore può essere puntato su qualsiasi altro array in qualsiasi momento. Vogliamo quel tipo di comportamento per l'argomento, invece (perché questa funzione non cambia nessuno di questi numeri interi). La dichiarazione segue l'uso.
(ps: sentiti libero di migliorare questo campione!)