Pasar arreglos y matrices a funciones como punteros y punteros a punteros en C

5 minutos de lectura

Dado el siguiente código:

void
foo( int* array ) 
{
    // ...
}

void
bar( int** matrix ) 
{
    // ...
}

int
main( void ) {
    int array[ 10 ];
    int matrix[ 10 ][ 10 ];

    foo( array );
    bar( matrix );

    return 0;
}

No entiendo por qué me sale esta advertencia:

advertencia: pasar el argumento 1 de ‘barra’ del tipo de puntero incompatible

Aunque la llamada ‘foo’ parece estar bien.

Gracias 🙂

avatar de usuario
Johannes Schaub – litb

Bueno, ciertamente no es bien entendido por la comunidad C como se puede ver al echar un vistazo a SO. La magia es, todos los siguientes son totalmente, 100%, equivalentes:

void foo(int (*array)[10]);
void foo(int array[][10]);
void foo(int array[10][10]);
void foo(int array[42][10]);

Está muy Es importante establecer la distinción entre un puntero y una matriz. Una matriz no es un puntero. Una matriz se puede convertir en un puntero a su primer elemento. Si tienes un puntero tienes esto:

--------
| ptr  |  -------> data
--------

Sin embargo, si tiene una matriz, tiene esto:

---------------------------
| c1 | c2 | c3 | ... | cn |
---------------------------

Con el puntero, los datos están en otro planeta, pero vinculados por el puntero. Una matriz tiene los datos en sí. Ahora, una matriz multidimensional es solo una matriz de matrices. Las matrices están anidadas en una matriz principal. Entonces, el tamaño de su matriz es:

(sizeof(int) * 10) * 10

Eso es porque tiene 10 arreglos, todos los cuales son arreglos de 10 enteros. Ahora, si desea pasar esa matriz, se convierte. ¿Pero a qué? Un puntero a su primer elemento. El tipo de elemento es no un puntero, sino una matriz. Como consecuencia, pasa un puntero a una matriz de 10 int:

int (*)[10] // a pointer to an int[10]

No es ni una matriz de int*ni un int**. Puede preguntar por qué la matriz no se pasa como un int**. Es porque el compilador tiene que saber la longitud de la fila. si haces un array[1][0]el compilador se dirigirá a un lugar sizeof(int) * 10 bytes aparte del comienzo de la matriz bidimensional. Decodifica esa información en el tipo de puntero a matriz.

Por lo tanto, debe elegir entre uno de los prototipos de funciones totalmente equivalentes anteriores. Naturalmente, el último es simplemente confuso. El compilador simplemente ignora silenciosamente cualquier número escrito en la dimensión más externa si un parámetro se declara como una matriz. Así que tampoco usaría la penúltima versión. Lo mejor es usar la primera o la segunda versión. Lo que es importante recordar es que C no tiene parámetros de matriz (reales)! El parámetro será un puntero al final (puntero a matriz en este caso).

Observe cómo el caso multidimensional de arriba es similar al caso degenerado y unidimensional de abajo. Todas las siguientes 4 versiones son totalmente equivalentes:

void foo(int *array);
void foo(int array[]);
void foo(int array[10]);
void foo(int array[42]);

  • Aunque la respuesta de Mark Pim se centra completamente en el problema principal de mi pregunta, quiero compensar de alguna manera todo su esfuerzo por tratar de explicar las sutilezas de las matrices como parámetros en C configurando esta respuesta como se recomienda. ¡Gracias!

    – Aurón

    14 de febrero de 2009 a las 0:39

avatar de usuario
marca pim

Pasar matrices multidimensionales en C es un tema complicado. Ver estas preguntas frecuentes.

La pregunta que debe hacerse es cómo usará bar. Si siempre sabe que se le pasará una matriz de 10×10, vuelva a escribirlo como

bar(int matrix[10][10]);

Si desea hacer frente a matrices de diferentes dimensiones, es posible que deba pasar las longitudes:

bar(int *matrix, int width, int height);

  • La tuya es una gran respuesta. ¡Gracias!

    – Aurón

    14 de febrero de 2009 a las 0:40

El problema es que la matriz de estructura de datos[10][10] en realidad no es una tabla de diez punteros a la matriz[10]’s, pero es una matriz secuencial de 100 enteros. La firma adecuada para bar es

bar (int matrix[10][10])

Si realmente desea representar la matriz usando indirección y tiene int **matrix como el tipo de parámetro para la barra, entonces debe asignarlo de manera diferente:

int *matrix[10];
int my_data[100];
int i;
for (i = 0; i < 10; i++) { matrix[i] = &(my_data[i * 10]); }
bar(matrix);

Ahora ‘matriz’ coincide con el tipo int **. ‘matriz’ es una matriz de diez punteros, y puede pasarla por puntero, obteniendo así el segundo *.

  • “El problema es que la matriz de estructura de datos[10][10] en realidad no es una tabla de diez punteros a la matriz[10]’s, pero es una matriz secuencial de 100 enteros.” ¡Por eso estaba tan confundido!

    – Aurón

    13 de febrero de 2009 a las 18:54

Aquí hay un código para practicar: contiene todos los tipos posibles de matriz bidimensional y código para acceder a los valores de los elementos.

#include <stdio.h>

#define NUMROWS 2
#define NUMCOLUMNS 5

#define FILL_ARRAY() \
    *array[0] = '1'; \
    (*array)[7] = '2'; \
    *(array[1]) = '3'; \
    *(*(array+1)+1) = '4'; \
    *(array[0]+3) = '5'; \
    *(*array+2) = '7'; \
    array[0][1] = '6'; 

void multi_01( char (*array)[NUMCOLUMNS] )       { FILL_ARRAY(); }
void multi_02( char array[][NUMCOLUMNS] )        { FILL_ARRAY(); }
void multi_03( char array[NUMROWS][NUMCOLUMNS] ) { FILL_ARRAY(); }
void multi_04( char **array )                    { FILL_ARRAY(); }
void multi_05( char *array[] )                   { FILL_ARRAY(); }
void multi_06( char *array[NUMCOLUMNS] )         { FILL_ARRAY(); }

int main(int argc, char **argv)
{
    int i;
    char mystr[NUMROWS][NUMCOLUMNS] = { { 'X', 'X', 'X', 'X'}, {'X','X','X'} };
    char *pmystr[sizeof(mystr)/sizeof(*mystr)];
    int numcolumns = sizeof(*mystr);
    int numrows = sizeof(mystr)/sizeof(*mystr);
    for( i=0; i<numrows; i++ ) pmystr[i] = *(mystr+i);

    multi_01( mystr );  multi_02( mystr );  multi_03( mystr );
    multi_04( pmystr ); multi_05( pmystr ); multi_06( pmystr );

    printf("array '%s', '%s'\n", mystr[0], mystr[1]);

    getc(stdin);
    return 0;
}

Debes definir la barra como:

bar( int* matrix )

En C, todas las matrices deben pasarse como int* (o type_of_element* para otros tipos).

int ** estaría bien si sus datos fueran realmente una matriz de punteros. int[*data[] por ejemplo. Eso es lo que obtienes main(int argc, char *argv[]).

avatar de usuario
dwc

int **matrix

indicaría que tiene un puntero a un puntero a int. Eso se usa comúnmente para indicar un puntero a una matriz de punteros (también llamado vector). Definitivamente NO es el caso con

int matrix[10][10]

que es más un puntero a una sola sección de memoria con un tamaño de 10×10 entradas. Intenta cambiar a:

void bar(int *matrix[])

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad