2010-10-12 17 views
5

si la siguiente es posible:La confusión acerca de los punteros y matrices multidimensionales

MyFunction(int *array, int size) 
{ 
    for(int i=0 ; i<size ; i++) 
    { 
     printf(“%d”, array[i]); 
    } 
} 

main() 
{ 
    int array[6] = {0, 1, 2, 3, 4, 5}; 
    MyFunction(array, 6); 
} 

Por qué los siguientes no es?

MyFunction(int **array, int row, int col) 
{ 
    for(int i=0 ; i<row ; i++) 
    { 
     for(int j=0 ; j<col ; j++) 
     { 
      printf(“%d”, array[i][j]); 
     } 
    } 
} 

main() 
{ 
    int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; 
    MyFunction(array, 3, 3); 
} 
+0

Encontré esto muy útil al iniciar la programación en C: http://cslibrary.stanford.edu/102/PointersAndMemory.pdf – helpermethod

+0

eche un vistazo a la sección array vs puntero del c-faq (http: // c- faq.com/aryptr/index.html) – pmg

Respuesta

10

primer lugar, algunos standard idioma:

6.3.2.1 Lvalues, arrays y designadores de función
...
3 Excepto cuando es el operando del operador sizeof o la unario y operador, o es un literal de cadena usado para inicializar una matriz, una expresión que tiene tipo "matriz de tipo" se convierte en una expresión con tipo "puntero a tipo" que apunta al elemento inicial del objeto matriz y no es un lvalue Si el objeto de matriz tiene una clase de almacenamiento de registro, el comportamiento no está definido.

Dada la declaración

int myarray[3][3]; 

el tipo de myarray es "matriz de 3 elementos de la matriz de 3 elementos de int".A juzgar por la regla anterior, cuando se escribe

MyFunction(myarray, 3, 3); 

la expresiónmyarray tiene su tipo implícitamente convertido ("descomposición") de "matriz de 3 elementos de la matriz de 3 elementos de int" a "puntero a 3 -elemento matriz de int "o int (*)[3].

De este modo, el prototipo de la función tendría que ser

int MyFunction(int (*array)[3], int row, int col) 

Tenga en cuenta que es int **arrayno lo mismo que int (*array)[3]; la aritmética del puntero será diferente, por lo que sus subíndices no terminarán apuntando a los lugares correctos. Recuerde que la indexación de matriz es definida en términos de aritmética de puntero: a[i] == *(a+i), a[i][j] == *(*(a + i) + j). a+i arrojará un valor diferente dependiendo de si a es int ** o int (*)[N].

Este ejemplo particular asume que siempre está pasando una matriz de elementos Nx3 de int; no es terriblemente flexible si quieres tratar con cualquier matriz de tamaño NxM. Una forma de evitar esto sería pasar explícitamente la dirección del primer elemento de la matriz, por lo que sólo estamos pasando un puntero simple, y luego calcular el desplazamiento adecuado de forma manual:

void MyFunction(int *arr, int row, int col) 
{ 
    int i, j; 
    for (i = 0; i < row; i++) 
    for (j = 0; j < col; j++) 
     printf("%d", a[i*col+j]); 
} 

int main(void) 
{ 
    int myarray[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; 
    ... 
    MyFunction(&myarray[0][0], 3, 3); 

Como nos pase un puntero simple a int, no podemos usar un doble subíndice en MyFunc; el resultado de arr[i] es un número entero, no un puntero, por lo que tenemos que calcular el desplazamiento completo en la matriz en la operación de un subíndice. Tenga en cuenta que este truco solo funcionará para matrices realmente multidimensionales.

Ahora, un **puede indicar valores que están organizados en una estructura en 2-D, pero que se construyó de una manera diferente. Por ejemplo:

void AnotherFunc(int **arr, int row, int col) 
{ 
    int i, j; 
    for (i = 0; i < row; i++) 
    for (j = 0; j < col; j++) 
     printf("%d", arr[i][j]); 
} 

int main(void) 
{ 
    int d0[3] = {1, 2, 3}; 
    int d1[3] = {4, 5, 6}; 
    int d2[3] = {7, 8, 9}; 

    int *a[3] = {d0, d1, d2}; 

    AnotherFunc(a, 3, 3); 
    ... 
} 

A juzgar por la regla anterior, cuando las expresiones d0, d1 y d2 aparecen en el inicializador para a, sus tipos son todos convertidos de "matriz de 3 elementos de int" a "puntero a int ". De forma similar, cuando aparece la expresión a en la llamada al AnotherFunc, su tipo se convierte de "matriz de puntero de 3 elementos a int" en "puntero a puntero a int".

Tenga en cuenta que en AnotherFunc subcribimos ambas dimensiones en lugar de calcular el desplazamiento como lo hicimos en MyFunc. Eso es porque a es una matriz de puntero valores. La expresión arr[i] nos proporciona el 0thi puntero valor de desplazamiento desde la ubicación arr; luego encontramos el j'th valor entero compensado de ese valor de puntero.

La siguiente tabla puede ayudar - que muestra los tipos de las diversas expresiones de la matriz y lo que se desintegran a la base de sus declaraciones (T (*)[N] es un tipo de puntero, no un tipo de matriz, por lo que no se descompone):

 
Declaration   Expression   Type   Implicitly Converted (Decays) to 
-----------   ----------   ----   -------------------------------- 
    T a[N]      a   T [N]   T * 
           &a   T (*)[N] 
           *a   T 
          a[i]   T 

    T a[M][N]      a   T [M][N]  T (*)[N] 
           &a   T (*)[M][N] 
           *a   T [N]   T * 
          a[i]   T [N]   T * 
          &a[i]   T (*)[N] 
          *a[i]   T 
          a[i][j]   T 

T a[L][M][N]     a   T [L][M][N]  T (*)[M][N] 
           &a   T (*)[L][M][N] 
           *a   T [M][N]  T (*)[N] 
          a[i]   T [M][N]  T (*)[N] 
          &a[i]   T (*)[M][N] 
          *a[i]   T [N]   T * 
          a[i][j]   T [N]   T * 
         &a[i][j]   T (*)[N] 
         *a[i][j]   T 
         a[i][j][k]   T 

El patrón para las matrices de mayor dimensión debe ser claro.

5

Editar: Aquí está mi intento de una respuesta más a la de punto a lo solicitado y se basa en el nuevo código de ejemplo:

Independientemente de la dimensiones de matriz, lo que pasa es un "puntero a una matriz": es solo un puntero único, aunque el tipo de puntero puede variar.

En su primer ejemplo, int array[6] es una matriz de 6 elementos int. Pasar array pasa un puntero al primer elemento, que es int, por lo tanto, el tipo de parámetro es int *, que se puede escribir de manera equivalente como int [].

En su segundo ejemplo, int array[3][3] es una matriz de 3 filas (elementos), cada una contiene 3 int s. Pasando array pasa un puntero al primer elemento, que es una matriz de 3 int s. Por lo tanto, el tipo es int (*)[3] - un puntero a una matriz de 3 elementos, que se puede escribir de manera equivalente como int [][3].

Espero que vea la diferencia ahora. Cuando pasa un int **, en realidad es un puntero a una matriz de int * sy NOT un puntero a una matriz 2D.

Un ejemplo para una real int ** sería algo como esto:

int a[3] = { 1, 2, 3 }; 
int b[3] = { 4, 5, 6 }; 
int c[3] = { 7, 8, 9 }; 
int *array[3] = { a, b, c }; 

Aquí array es una matriz de 3 int * s, y pasando como argumento podría resultar en un int **.


Respuesta original:

Su primer ejemplo no es realmente una matriz 2D, aunque se utiliza de una manera similar. Allí, está creando ROWS punteros char *, cada uno de los cuales apunta a una matriz diferente de COLS caracteres. Aquí hay dos niveles de indirección.

El segundo y el tercer ejemplo son en realidad matrices en 2D, donde la memoria para los caracteres completos ROWS * COLS es contigua. Solo hay un nivel de indirección aquí.Un puntero a una matriz 2D no es char **, pero char (*)[COLS], por lo que puede hacer:

char (*p)[SIZE] = arr; 
// use p like arr, eg. p[1][2] 
0

El primer ejemplo es posible debido a que las matrices se degeneran a los punteros cuando se pasan como parámetros de la función.

El segundo ejemplo no funciona porque int[3][3] degenera a int (*)[3], no un doble puntero int **. Este es el caso porque las matrices 2D son contiguas en la memoria, y sin esta información el compilador no sabría cómo acceder a los elementos más allá de la primera fila. Considere una simple rejilla de números:

1 2 6 
0 7 9 

Si nos almacenando estos números en una matriz int nums[6], ¿cómo podríamos índice en la matriz para acceder al elemento 7? Por 1 * 3 + 1, por supuesto, o más en general, row * num-columns + column. Para acceder a cualquier elemento más allá de la primera fila, necesita saber cuántas columnas tiene la grilla.

Cuando almacena los números como nums[2][3], el compilador usa exactamente la misma aritmética row * num-columns + column que manualmente con una matriz 1D, simplemente está oculta del programador. Por lo tanto, debe pasar el número de columnas al pasar una matriz 2D para que el compilador pueda realizar esta aritmética.

En muchos otros idiomas, las matrices llevan información sobre su tamaño, lo que elimina la necesidad de especificar manualmente las dimensiones al pasar matrices multidimensionales a las funciones.

0

Quizás podamos esperar una pregunta más "al punto", si usted quiere tener una respuesta más al punto. Su idea tiene dos problemas:

  1. una matriz 2D int A[3][3] cuando se utiliza en una expresión decae a la dirección de su primer elemento de este modo a un puntero de tipo int (*)[3]. Para poder pasar la matriz, debe usar &A[0][0] para obtener un puntero al primer miembro "interno".
  2. dentro de su función la operación A[i][j] no se puede realizar desde su compilador no tiene información de la longitud de fila, allí.
1

Los demás lo han resumido. int ** A significa que A es un puntero a una matriz y no una referencia a una matriz 2-D. Sin embargo, eso no significa que no sea utilizable. Dado que los datos en C se almacenan en orden mayor de fila, una vez que conoce la longitud de fila, recuperar los datos debe ser fácil

0

Hay dos problemas principales con este código.

MyFunction(int **array, int row, int col); 

La primera es que int **array es del tipo incorrecto para usar. Este es un puntero a un puntero, mientras que

int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; 

es una matriz multidimensional. La memoria que compone esta matriz multidimensional es todo un fragmento, y el desplazamiento desde el comienzo de este hasta cualquier elemento de esta matriz se calcula según el conocimiento del tamaño de una fila en esta matriz.

int *A[99]; 

Esta es una matriz de punteros a números enteros. Los enteros apuntados podrían ser el primero de varios enteros en la memoria, lo que significa que estos realmente apuntan a matrices de enteros.

En muchas circunstancias, cuando utiliza el nombre de una matriz en un programa, se evalúa como un puntero al comienzo de la matriz. Si usted dice:

int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; 
printf("%p %p %p\n", array, array[0], &(array[0][0])); 

Usted debe obtener la misma dirección impreso 3 veces porque todos ellos se refieren a la misma dirección, pero sus tipos no son los mismos. El tipo de datos de los dos últimos es similar y compatible para muchos propósitos, ya que array[0] se trataría como un puntero al primer elemento de la primera fila de array y esta fila es una matriz por sí misma.

Si dicen:

int **A; 

Usted está diciendo que hay un puntero a un puntero a un int. Mientras A[2][4] es una expresión válida, esto no es una matriz multidimensional de la misma manera como:

int B[3][3]; 

Si dicen A[1] esto se evalúa como un int * similar a B[1] haría, excepto que se puede decir A[1] = (int *)0x4444;, pero si dijo B[1] = (int *)0x4444; que obtendría un error de compilación porque B[1] es en realidad un valor calculado, no una variable. Con B no hay una matriz de variables int *, solo algunos cálculos basados ​​en el tamaño de fila y la dirección del primer miembro de la matriz.

Este código debería hacer algo similar a lo que quería (algunos cambios de formato de salida para la legibilidad). Observe cómo se cambia el valor del índice en la declaración de impresión.

MyFunction(int *array, int row, int col) 
{ 
    int x = 0; 
    for(int i=0 ; i<row ; i++) 
    { 
     for(int j=0 ; j<col ; j++) 
     { 
      printf(“%d ”, array[x++]); 
     } 
     printf("\n"); 
    } 
} 

main() 
{ 
    int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; 
    MyFunction(array, 3, 3); 
} 
Cuestiones relacionadas