2009-02-13 21 views
14

Dado el siguiente código:matrices y matrices a funciones como punteros y punteros Pasando a los punteros en C

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 da esta advertencia:

advertencia: se pasa el argumento 1 de 'barra' desde el tipo de puntero incompatible

Aunque la llamada 'foo' parece estar bien.

Gracias :)

Respuesta

43

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%, lo que equivale:

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

Es muy importante establecer la distinción de un puntero y una matriz. Una matriz no es un puntero. Una matriz puede convertirse en un puntero a su primer elemento. Si usted tiene un puntero que tiene esto:

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

Sin embargo, si usted tiene una matriz, que tienen la siguiente:

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

Con el puntero, los datos es en conjunto el otro planeta, pero vinculados a 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. Por lo tanto, el sizeof de la matriz es:

(sizeof(int) * 10) * 10 

Eso es porque tienes 10 arrays, todos los cuales son conjuntos de 10 enteros. Ahora, si quieres pasar esa matriz, se convierte. Pero a que? Un puntero a su primer elemento. El tipo de elemento es no un puntero, sino una matriz. Como consecuencia, se pasa un puntero a un array de 10 int:

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

No es ni un conjunto de int*, ni un int**. Puede preguntar por qué la matriz no se pasa como int**. Es porque el compilador tiene que saber la longitud de la fila. Si realiza un array[1][0], el compilador se dirigirá a un lugar sizeof(int) * 10 bytes aparte del comienzo de la matriz bidimensional. Descodifica 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 ignora silenciosamente cualquier número escrito en la dimensión más externa si un parámetro se declara como una matriz. Entonces tampoco usaría la segunda versión anterior. Lo mejor es usar la primera o la segunda versión. Lo que es importante recordar es que C no tiene parámetros (reales) de matriz! 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 de una dimensión a continuación. Todas las 4 versiones siguientes son completamente equivalentes:

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

Aunque la respuesta de Mark Pim se centra completamente en el problema principal de mi pregunta, quiero compensar de alguna manera todo su esfuerzo tratando de explicar los subtelios de matrices como parámetros en C estableciendo esta respuesta como se recomienda. ¡Gracias! – Auron

1

Debe definir barras como:

bar(int* matrix) 

En todas las matrices C debe pasar como int* (o type_of_element* para otros tipos).

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

6

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], sino que es una matriz secuencial de 100 enteros. La firma apropiada para la barra es

bar (int matrix[10][10]) 

Si realmente se quiere representar la matriz utilizando direccionamiento indirecto y tienen int ** matriz como el tipo de parámetro para la barra, a continuación, es necesario asignar 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 **. 'matrix' es una matriz de diez punteros, y puede pasarla por puntero, obteniendo así el segundo *.

+0

"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], sino que es una matriz secuencial de 100 enteros ". ¡Por eso estaba tan confundido! – Auron

14

Pasar matrices multidimensionales en C es un tema complicado. Ver this FAQ.

La pregunta que debe hacer es cómo va a utilizar bar. Si siempre sabe que va a pasar una matriz de 10x10 y vuelva a grabar como

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

Si desea hacer frente a las matrices de dimensiones variables a continuación, es posible que tenga que pasar en las longitudes:

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

La tuya es una gran respuesta. ¡Gracias! – Auron

-1
int **matrix 

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

int matrix[10][10] 

que es una más de un puntero a una única sección de memoria de tamaño de 10x10 enteros. Intente cambiar a:

void bar(int *matrix[]) 
+0

bar evalúa a int ** matrix y no es cómo pasaría la matriz – Ulterior

2

Aquí hay un código para practicar - contiene todas las posibilidades de pasar gama 2dimensional y el código para acceder a los valores de 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; 
}