¿Por qué c/c++ permite la omisión del índice más a la izquierda de una matriz multidimensional en una llamada de función?

6 minutos de lectura

avatar de usuario de vjain27
vjain27

Me preguntaba por qué se permite omitir el índice más a la izquierda de una matriz multidimensional al pasar la matriz a una función. ¿Por qué no más de un índice? ¿Y cómo averigua el compilador el tamaño con un índice omitido?

  • ¿Podría proporcionar algún código como ejemplo?

    – Drahakar

    20 de abril de 2011 a las 23:47

avatar de usuario de hugomg
Hugo

Otras respuestas describieron cómo el estándar C maneja la conversión de matriz a puntero y cómo esto afecta la declaración de la función, pero creo que no entraron en el por quéasí que ahí voy…

En C, las matrices representan elementos estrechamente empaquetados en la memoria.

A -> _ _ _ _ _ _ ...
i:   0 1 2 3 4 5 ...

En el ejemplo anterior, cada uno de los elementos de la matriz es 1 _ ancho. Para encontrar el i-ésimo elemento, tenemos que ir a la i-ésima dirección. (Tenga en cuenta que la dimensión más a la izquierda (el tamaño) no importa aquí)

Ahora considere una matriz multidimensional:

B -> [_ _ _][_ _ _][_ _ _][_ _ _]...
i:    0 0 0  1 1 1  2 2 2  3 3 3
j:    0 1 2  0 1 2  0 1 2  0 1 2
     ^first row    ^third row

Para encontrar el desplazamiento de A[i][j] necesitamos saltar sobre i filas (3*i) y luego sobre j elementos -> (3*i + j). Tenga en cuenta que el tamaño de la primera dimensión tampoco es necesario aquí.

Entonces debería quedar claro que el tamaño más a la izquierda no es necesario cuando se usa la matriz, solo es necesario cuando lo creas.


Como no hay necesidad para dar la dimensión del índice más a la izquierda, entonces ¿por qué no darlo de todos modos, en aras de la integridad? Después de todo, esto es lo que se hace en el lenguaje de programación Pascal (un C contemporáneo).

Bueno, la mayoría de las funciones que operan en matrices funcionan de la misma manera para todas las longitudes de matriz posibles, por lo que especificar el tamaño solo perjudicaría su capacidad para reutilizarlas.

por ejemplo, ¿por qué

int sum(int arr[10]){
    int s = 0, i;
    for(i=0; i<10; i++){
        s += arr[i];
    }
    return s;
}

Cuando puedes hacer esto en su lugar:

int sum(int arr[], int n){
    int s = 0, i;
    for(i=0; i<n; i++){
        s += arr[i];
    }
    return s;
}

En cuanto a omitir más de una dimensión, esto no es posible cuando se usan matrices multidimensionales normales (porque necesita conocer la dimensión para saber cuándo termina la primera fila y comienza la segunda). Sin embargo, si está dispuesto a gastar algo (poco) de memoria adicional para el espacio disponible, es perfectamente posible usar punteros a punteros en su lugar: http://www.eskimo.com/~scs/cclass/int/sx9b.html

  • Tu dibujo ayuda muchísimo. Muchas gracias

    – marco m

    12 de agosto de 2011 a las 14:36

Lightness Races en el avatar de usuario de Orbit
Carreras de ligereza en órbita

en una declaración

En realidad, no puede omitir por completo la dimensión más a la derecha o la más a la izquierda.

Sin embargo, el más a la izquierda solo se puede deducir para usted si tiene un inicializador.

En una lista de argumentos de función

Cuando pasa una matriz a una función por valor, en realidad está pasando un puntero al primer elemento de esa matriz. Sí, por la sintaxis parece que está pasando una matriz, pero no, no es así.

Considerar:

void f(int ar[3])

void f(int ar[])

Ambos son una sintaxis confusa para el equivalente:

void f(int* ar)

Ni rastro de una matriz, y mucho menos de uno de tres elementos específicos.

Ahora:

void f(int ar[][3])

Esta es una sintaxis confusa para el equivalente:

void f(int (*ar)[3])

dónde int (*)[3] es el tipo de un puntero al primer elemento de su matriz (puntero a int[3]).

En conclusión, no preste demasiada atención a la sintaxis tipo matriz que parece []; no es realmente representativo de lo que realmente está sucediendo.

  • “la declaración de ‘matriz’ como matriz multidimensional debe tener límites para todas las dimensiones excepto el primero“¿Dónde hay pruebas de que el primero no puede?… ideone.com/kR76X … ah. Recuerdo vagamente que se trata como un puntero si solo tiene una dimensión y lo dejas en blanco.

    – mpen

    20 de abril de 2011 a las 23:59

  • Qué pasa void foo(int bar[][3]);?

    – Felipe

    21 de abril de 2011 a las 0:00

  • @Philip: Pfft, la parte sobre él pasando a una función es una edición de la pregunta. Agregaré a mi respuesta.

    – Carreras de ligereza en órbita

    21 de abril de 2011 a las 0:01

  • @Tomalak: No noté la edición del OP. Ciertamente no tenía la intención de insinuarte algo, solo tenía curiosidad.

    – Felipe

    21 de abril de 2011 a las 0:04


  • @Mark: estás pensando en cuándo una función tiene un argumento int ar[]; sin embargo, esto no está relacionado con las declaraciones. 🙂

    – Carreras de ligereza en órbita

    21 de abril de 2011 a las 0:06

Excepto cuando es el operando del sizeof o unario & operadores, o es un literal de cadena que se utiliza para inicializar una matriz en una declaración, una expresión de tipo “matriz de elementos N de T” tendrá su tipo implícitamente convertido a “puntero a T y evaluará la dirección del primer elemento en la matriz.

¿Qué tiene que ver todo eso con tu pregunta?

Supongamos las siguientes líneas de código:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
foo(arr);

Pasamos la expresión matricial arr como argumento para foo. Desde arr no es operando de ninguno sizeof o &su tipo se convierte implícitamente de “matriz de 10 elementos de int“a” puntero a int“. Por lo tanto, estamos pasando un puntero valor a foono una matriz.

Resulta que en la declaración de un parámetro de función, T a[] y T a[N] son sinónimos de T *a; los tres declaran a como un puntero a Tno una matriz de T.

Podemos escribir la definición del prototipo para foo como

void foo(int *a)     // <- foo receives a pointer to int, not an array

o

void foo(int a[])    // <-- a[] is a synonym for *a

Ambos significan lo mismo; ambos declaran a como un puntero a int.

Ahora echemos un vistazo a las matrices multidimensionales. Asuma el siguiente código:

int arr[10][20];
foo(arr);

La expresion arr tiene tipo “matriz de 10 elementos de matriz de 20 elementos de int“. Según la regla descrita anteriormente, implícitamente se convertirá en “puntero a una matriz de 20 elementos de int“. Por lo tanto, la definición de prototipo para foo Se puede escribir como

void foo(int (*a)[20])  // <-- foo receives a pointer to an array, not an array of arrays

o

void foo(int a[][20])  // <-- a[][20] is a synonym for (*a)[20]

De nuevo, ambos declaran a como un punterono una matriz.

Esta es la razón por la que puede soltar el extremo izquierdo (y solo el más a la izquierda) índice de matriz en una declaración de parámetro de función.

¿Ha sido útil esta solución?