La respuesta para los dos últimos también se puede deducir de la regla de oro en C:
La declaración sigue al uso.
int (*arr2)[8];
¿Qué pasa si desreferencias arr2
? Obtiene una matriz de 8 enteros.
int *(arr3[8]);
¿Qué sucede si tomas un elemento de arr3
? Obtienes un puntero a un entero.
Esto también ayuda cuando se trata de punteros a funciones. Para tomar el ejemplo de sigjuice:
float *(*x)(void )
¿Qué pasa cuando desreferencias x
? Obtiene una función que puede llamar sin argumentos. ¿Qué pasa cuando lo llamas? Devolverá un puntero a a float
.
Sin embargo, la precedencia del operador siempre es complicada. Sin embargo, el uso de paréntesis también puede ser confuso porque la declaración sigue al uso. Al menos, para mí, intuitivamente se arr2
ve como una matriz de 8 punteros a ints, pero en realidad es al revés. Solo toma un tiempo acostumbrarse. Razón suficiente para agregar siempre un comentario a estas declaraciones, si me preguntas :)
editar: ejemplo
Por cierto, me topé con la siguiente situación: una función que tiene una matriz estática y que usa la aritmética del puntero para ver si el puntero de la fila está fuera de los límites. Ejemplo:
#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;
}
Salida:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Tenga en cuenta que el valor del borde nunca cambia, por lo que el compilador puede optimizarlo. Esto es diferente de lo que podría querer usar inicialmente const int (*border)[3]
:: que declara el borde como un puntero a una matriz de 3 enteros que no cambiará el valor mientras exista la variable. Sin embargo, ese puntero puede apuntar a cualquier otra matriz de este tipo en cualquier momento. En cambio, queremos ese tipo de comportamiento para el argumento (porque esta función no cambia ninguno de esos enteros). La declaración sigue al uso.
(pd: ¡siéntase libre de mejorar esta muestra!)