295 votos

Desambiguación del puntero C a la Matriz/Matriz de punteros

¿Cuál es la diferencia entre las siguientes declaraciones?

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

¿Cuál es la regla general para entender las declaraciones más complejas?

277voto

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

La tercera es igual que la primera.

La regla general es precedencia de los operadores . Puede ser incluso mucho más complejo cuando los punteros de función entran en escena.

204voto

sigjuice Puntos 9166

Utiliza el programa cdecl, como sugiere 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>

También funciona a la inversa.

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

84voto

GManNickG Puntos 155079

No sé si tiene un nombre oficial, pero yo lo llamo el Right-Left Thingy(TM).

Empieza por la variable, luego ve a la derecha, y a la izquierda, y a la derecha... y así sucesivamente.

int* arr1[8];

arr1 es un array de 8 punteros a enteros.

int (*arr2)[8];

arr2 es un puntero (el paréntesis bloquea la derecha-izquierda) a un Array de 8 enteros.

int *(arr3[8]);

arr3 es un array de 8 punteros a enteros.

Esto debería ayudarte con las declaraciones complejas.

18voto

Sunil bn Puntos 101
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

9voto

Hraban Luyat Puntos 11

La respuesta para las dos últimas también se puede deducir de la regla de oro en C:

La declaración sigue al uso.

int (*arr2)[8];

¿Qué sucede si se hace referencia a arr2? Obtienes un Array de 8 enteros.

int *(arr3[8]);

¿Qué ocurre si se toma 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é ocurre cuando se dereferencia x? Obtienes una función que puedes llamar sin argumentos. ¿Qué ocurre cuando la llamas? Que devolverá un puntero a un flotador.

Sin embargo, la precedencia de los operadores 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 arr2 parece un Array de 8 punteros a ints, pero en realidad es al revés. Sólo hay que acostumbrarse. Razón suficiente para añadir siempre un comentario a estas declaraciones, en mi opinión :)

editar: ejemplo

Por cierto, acabo de tropezar con la siguiente situación: una función que tiene una matriz estática y que utiliza la aritmética de punteros 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;
}

La 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 de border nunca cambia, por lo que el compilador puede optimizarlo. Esto es diferente de lo que usted podría querer usar inicialmente: const int (*border)[3] que declara la frontera como un puntero a un Array de 3 enteros que no cambiará de valor mientras exista la variable. Sin embargo, ese puntero puede apuntar a cualquier otro Array de este tipo en cualquier momento. Nosotros queremos ese tipo de comportamiento para el argumento, en cambio (porque esta función no cambia ninguno de esos enteros). La declaración sigue al uso.

(p.d.: siéntase libre de mejorar esta muestra)

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X