Pregunta Confusión sobre punteros y matrices multidimensionales


Si lo siguiente es posible:

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é lo siguiente 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);
}

5
2017-10-12 02:17


origen


Respuestas:


Primero, algo estándar idioma:

6.3.2.1 Valores L, matrices y designadores de funciones
...
3 Excepto cuando es el operando del operador sizeof o unary & operator, o es un literal de cadena usado para inicializar una matriz, una expresión que tiene el tipo "array of type" se convierte en una expresión con el tipo "pointer to type" que apunta al elemento inicial del objeto de matriz y no es un valor l. 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". Siguiendo la regla anterior, cuando escribes

MyFunction(myarray, 3, 3);

el expresión  myarray tiene su tipo convertido implícitamente ("decaimiento") de la "matriz de 3 elementos de la matriz de 3 elementos de int"a" puntero a la matriz de 3 elementos de int", o int (*)[3].

Por lo tanto, su prototipo de función tendría que ser

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

Tenga en cuenta que int **array es no 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 definido en términos de aritmética de puntero: a[i] == *(a+i), a[i][j] == *(*(a + i) + j). a+i dará un valor diferente dependiendo de si a es un int ** o un int (*)[N].

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

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 pasamos un simple puntero a int, no podemos usar un subíndice doble 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 **  poder indica los valores que están organizados en una estructura 2-D, pero uno que fue construido 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);
  ...
}

Siguiendo la regla anterior, cuando las expresiones d0, d1y d2 aparece en el inicializador para a, todos sus tipos se convierten de "matriz de 3 elementos de intpuntero "a" int". Del mismo modo, cuando la expresión a aparece en la llamada a AnotherFunc, su tipo se convierte de "matriz de 3 elementos de puntero a int"a" puntero a puntero para int".

Tenga en cuenta que en AnotherFunc Subíndimos ambas dimensiones en lugar de calcular el desplazamiento como hicimos en MyFunc. Eso es porque a es una matriz de puntero valores. La expresion arr[i] nos da el i'th puntero valor compensado de la ubicación arr; luego encontramos el j'th offset de valor entero a partir de ese valor de puntero.

La siguiente tabla podría ayudar: muestra los tipos de diversas expresiones de matriz y a qué se declinan según sus declaraciones (T (*)[N] es un tipo de puntero, no un tipo de matriz, por lo que no decae):

Tipo de expresión de declaración convertido implícitamente (decaimientos) a
----------- ---------- ---- ------------------------- -------
     T a [N] a T [N] T *
                               & a T (*) [N]
                               *a
                             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 matrices de dimensiones superiores debe ser claro.


11
2017-10-18 21:15



Editar: Aquí está mi intento de obtener una respuesta más precisa según lo solicitado y basado en su nuevo código de ejemplo:

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

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

En tu segundo ejemplo, int array[3][3] es una matriz de 3 filas (elementos), cada una de las cuales contiene 3 ints. Paso array pasa un puntero al primer elemento, que es una matriz de 3 ints. Por lo tanto, el tipo es int (*)[3] - un puntero a una matriz de 3 elementos, que puede escribirse de manera equivalente como int [][3].

Espero que veas la diferencia ahora. Cuando pasas una int **, es en realidad un puntero a una matriz de int *s y NO un puntero a una matriz 2D.

Un ejemplo para un 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 pasar esto como un argumento daría lugar a una int **.


Respuesta original:

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

El segundo y tercer ejemplo son en realidad arreglos 2D, donde la memoria para todo el ROWS * COLS Los caracteres son contiguos. Solo hay un nivel de indirección aquí. Un puntero a una matriz 2D no es char **, pero char (*)[COLS], entonces puedes hacer:

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

5
2017-10-12 02:32



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


1
2017-10-18 13:07



Porque un puntero de puntero no es el mismo tipo que un puntero a una matriz. Ver punteros a punteros y matrices de punteros para detalles.

Además, esto tiene una buena información: http://c-faq.com/aryptr/index.html


0
2017-10-12 02:20



El primer ejemplo es posible porque las matrices degeneran en punteros cuando se pasan como parámetros de función.

El segundo ejemplo no funciona porque int[3][3] degenerado 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. Considera una simple cuadrícula de números:

1  2  6 
0  7  9

Si estuviéramos almacenando estos números en una matriz int nums[6], ¿cómo podríamos indexar 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 el mismo row * num-columns + column aritmética como lo haces 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 lenguajes, 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
2017-10-18 04:55



Tal vez podamos esperar una pregunta más "al punto", si desea tener una respuesta más al punto. Tu idea tiene dos problemas:

  1. una matriz 2D int A[3][3] cuando se usa en una expresión decae a la dirección de su primer elemento por lo tanto a un puntero de tipo int (*)[3]. Para poder pasar la matriz a través tendrías que usar &A[0][0] para obtener un puntero al primer miembro "interno".
  2. dentro de tu funcion la operacion A[i][j] no se puede realizar desde tu compilador no tiene información de la longitud de la fila, allí.

0
2017-10-18 07:48



Hay dos problemas principales con este código.

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

La primera es que int **array es el 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 forma esta matriz multidimensional es todo un trozo, y el desplazamiento desde el principio de esta a cualquier elemento de esta matriz se calcula en base al conocimiento del tamaño de una matriz. fila en este conjunto.

int *A[99];

Esta es una matriz de punteros a enteros. Los enteros a los que se apunta podrían ser el primero de varios enteros en la memoria, lo que significa que en realidad apuntan a matrices de enteros.

En muchas circunstancias, cuando usa el nombre de una matriz en un programa, se convierte en un puntero al principio 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]) );

Debe obtener la misma dirección impresa 3 veces porque todas 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] sería tratado como un puntero al primer elemento de la primera fila de array y esta fila es una matriz por sí misma.

Si usted dice:

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, esta no es una matriz multidimensional de la misma manera que:

int B[3][3];

Si usted dice A[1] esto se evalúa a una int * Similar a B[1] lo haría, salvo que puedas decir A[1] = (int *)0x4444;, pero si dijiste B[1] = (int *)0x4444; obtendrías un error de compilación porque B[1] es en realidad un valor calculado, no una variable. Con B no hay una serie de int * variables: 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 modifica 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);
}

0
2017-10-18 17:22