Digamos que tengo dos matrices y las estoy pasando a una función:

void func(int arr1[][4], int **arr2) { // <- I need to give n in only one, why?
...
}
int main() {
    int n = 5, m = 4;
    int arr1[n][m];
    int **arr2 = (int**)malloc(n * sizeof(int*));
    for(int i = 0;i < n;i++)
        arr2[i] = (int*)malloc(m * sizeof(int));
    func(arr1, arr2);
    return 0;
}

¿Por qué no podemos tratar los dos arreglos pasando de manera similar?

Editar: Hubo un error en el código.

0
as2d3 15 jun. 2017 a las 09:21

3 respuestas

La mejor respuesta

En realidad, lo contrario de lo que estás diciendo es el caso: no tienes que pasar el número de filas. Suponga que los índices de matriz funcionan así:

int arr[MAX_ROW][MAX_COL]; /* with both 3 */

           col
     --------------->
    | 0,0   0,1   0,2
row | 1,0   1,1   1,2
    V 2,0   2,1   2,2

Cuando pasa int arr[][MAX_COL], el compilador sabe dónde comenzará la siguiente fila cuando aborde, por ejemplo, arr[row][col].

Si lo hiciera manualmente con un puntero, se vería así: &arr[0][0] + row * MAX_COL + col. En ese ejemplo, también debe conocer el tamaño de columna MAX_COL de la matriz para calcular la siguiente fila.

La razón de esto es que una matriz es continua en la memoria. La matriz anterior se representa en la memoria como:

|     row = 0     |     row = 1     |     row = 2     |
| 0,0   0,1   0,2 | 1,0   1,1   1,2 | 2,0   2,1   2,2 |

El compilador también debe conocer el desplazamiento de fila porque cuando pasa una matriz declarada como int arr[MAX_SIZE] a la función void foo (int arr[]), se desintegra en un puntero al comienzo de la matriz int* arr. En el caso de las matrices de matrices (matrices 2D), también se desintegra a un puntero a su primer elemento, que es un puntero a una matriz única int (*arr)[MAX_COL].

En resumen: con int arr[][MAX_COL] el compilador tiene toda la información necesaria para abordar la matriz con arr[row][col].

1
Andre Kampling 15 jun. 2017 a las 07:35

En realidad es lo contrario, puede omitir solo uno del índice (en el caso de una matriz multidimensional), el más interno.

Esto se debe a que las matrices, aunque se pasan como argumentos de función, se descomponen en un puntero al primer elemento. Citando C11, capítulo §6.3.2.1

Excepto cuando es el operando del operador sizeof, el operador _Alignof o el operador & unario, o es un literal de cadena utilizado para inicializar una matriz, una expresión que tiene tipo ‘‘ matriz de tipo '' se convierte en una expresión con tipo ‘‘ puntero para escribir '' que apunta al elemento inicial del objeto de matriz y no es un lvalue. [...]

Así, una notación como

 void func(int arr1[][5], int **arr2)    //an array of array of 5 ints

Y

 void func(int (*arr1) [5], int **arr2)  //pointer to the starting element of an array of 5 ints

Son equivalentes

0
Sourav Ghosh 15 jun. 2017 a las 06:47

En realidad, solo tiene una matriz de entradas (es decir, int arr1[][5]) y un puntero a un puntero de un int, es decir, int **arr2. Incluso si una matriz como arr1[10][5], cuando se pasa como argumento a una función, se desintegra a un puntero al comienzo de la memoria donde residen los elementos, hay una (gran) diferencia en el diseño de la memoria y en la forma en que El compilador trata el acceso a estos punteros.

Por cierto, en general debería ser int n=5,m=4;int arr1[m][n], no int n=5,m=4;int arr1[n][m].

Con respecto al diseño de la memoria:

Una matriz entera 2D de la forma int [10][5] se representa como 10 "filas" consecutivas, cada una de las cuales comprende 5 "columnas" (es decir, valores integrales). El tamaño de esta matriz es 10 * 5 * sizeof(int), y el tamaño de una "fila" es 5 * sizeof(int).

Un puntero a un puntero a int int **p es solo un puntero; su tamaño es sizeof(int**), incluso si ha "mal colocado" una secuencia de punteros integrales lile p = malloc(10 * sizeof (int*)); Tenga en cuenta "*" en sizeof(int *), ya que crea una secuencia de punteros a enteros, no una secuencia de enteros. Esa es la principal diferencia en el diseño de la memoria: no es una matriz 2D de enteros, sino una matriz 1D de punteros a enteros. Y si uno realmente hubiera asignado 10 "filas" para "10" enteros, cada fila podría ubicarse en una porción diferente de la memoria. El espacio necesario para gestionar una cantidad (extendida) de 10x5 valores integrales es "10 * sizeof (int *) + 10 * 5 * sizeof (int)".

Con respecto al acceso:

Supongamos una variable de tipo int arr[][5], que es una matriz 2D de enteros, donde el tamaño de una columna es 5 y el número de filas no está determinado. Informalmente, un acceso como int x = arr[3][4] se traduce en un acceso al elemento (3*5 + 4) th de la matriz, es decir, "fila veces fila tamaño más columna"; Tenga en cuenta que, según esta fórmula, el compilador no necesita saber cuántas filas tiene realmente la matriz.

En contraste, supongamos una variable de tipo int **p. Puede pensar en un acceso como x = p[3][4] como equivalente a int *r = p[3]; int x = r[4]; Tenga en cuenta que r es del tipo int *, es decir, es un puntero, y r[4] luego desreferencia este puntero y devuelve un valor integral.

Esto se describe informalmente. Sin embargo, el problema principal es que el diseño de memoria de un arr[][5] contiene solo valores integrales consecutivos, mientras que int **arrr puede ser una secuencia de punteros (o incluso uno de esos punteros), cada uno de ellos probablemente apunta a un secuencia de valores integrales (o solo un valor integral).

0
Stephan Lechner 15 jun. 2017 a las 07:03