Longitud de la matriz en el argumento de la función

Resuelto Jan Turoň asked hace 12 años • 9 respuestas

Este es un código bien conocido para calcular la longitud de una matriz en C:

sizeof(array)/sizeof(type)

Pero parece que no puedo averiguar la longitud de la matriz pasada como argumento a una función:

#include <stdio.h>

int length(const char* array[]) {
  return sizeof(array)/sizeof(char*);
}

int main() {
  const char* friends[] = { "John", "Jack", "Jim" };
  printf("%d %d", sizeof(friends)/sizeof(char*), length(friends)); // 3 1
}

Supongo que la matriz se copia por valor al argumento de la función como puntero constante y la referencia a ella debería resolver esto, pero esta declaración no es válida:

int length(const char**& array);

Considero que pasar la longitud de la matriz como segundo argumento es información redundante, pero ¿por qué la declaración estándar es mainasí?

int main(int argc, char** argv);

Explique si es posible averiguar la longitud de la matriz en el argumento de la función y, de ser así, por qué existe la redundancia main.


Jan Turoň avatar Nov 25 '11 19:11 Jan Turoň
Aceptado

sizeofsolo funciona para encontrar la longitud de la matriz si la aplica a la matriz original.

int a[5]; //real array. NOT a pointer
sizeof(a); // :)

Sin embargo, cuando la matriz se descomponga en un puntero, sizeof dará el tamaño del puntero y no de la matriz.

int a[5];
int * p = a;
sizeof(p); // :(

Como ya ha señalado inteligentemente, main recibe la longitud de la matriz como argumento (argc). Sí, esto es por necesidad y no es redundante . (Bueno, es algo redundante ya que argv termina convenientemente con un puntero nulo, pero estoy divagando)

Hay algún razonamiento de por qué esto sucedería. ¿Cómo podríamos hacer cosas para que una matriz C también conozca su longitud?

Una primera idea sería no tener matrices que se descompongan en punteros cuando se pasan a una función y continuar manteniendo la longitud de la matriz en el sistema de tipos. Lo malo de esto es que necesitaría tener una función separada para cada longitud posible de matriz y hacerlo no es una buena idea. (Pascal hizo esto y algunas personas piensan que esta es una de las razones por las que "perdió" frente a C)

Una segunda idea es almacenar la longitud de la matriz junto a la matriz, tal como lo hace cualquier lenguaje de programación moderno:

a -> [5];[0,0,0,0,0]

Pero entonces simplemente estás creando algo invisible structentre bastidores y la filosofía C no aprueba este tipo de gastos generales. Dicho esto, crear una estructura de este tipo por tu cuenta suele ser una buena idea para algunos tipos de problemas:

struct {
    size_t length;
    int * elements;
}

Otra cosa en la que puedes pensar es en cómo las cadenas en C terminan en nulo en lugar de almacenar una longitud (como en Pascal). Para almacenar una longitud sin preocuparse por los límites se necesitan la friolera de cuatro bytes, una cantidad inimaginablemente costosa (al menos en aquel entonces). Uno podría preguntarse si las matrices también podrían tener una terminación nula de esa manera, pero entonces, ¿cómo permitiría que la matriz almacene un valor nulo?

hugomg avatar Nov 25 '2011 12:11 hugomg

La matriz se descompone en un puntero cuando se pasa.

La sección 6.4 de las preguntas frecuentes de C cubre esto muy bien y proporciona las referencias de K&R, etc.


Aparte de eso, imagine que fuera posible que la función conociera el tamaño de la memoria asignada en un puntero. Podrías llamar a la función dos o más veces, cada vez con diferentes matrices de entrada que tenían longitudes potencialmente diferentes; por lo tanto, la longitud tendría que pasarse de alguna manera como una variable oculta secreta. Y luego considere si pasó un desplazamiento a otra matriz, o una matriz asignada en el montón ( mallocy todas son funciones de biblioteca, algo a lo que el compilador se vincula, en lugar de ver y razonar sobre el cuerpo).

Se está volviendo difícil imaginar cómo podría funcionar esto sin algunos objetos de corte detrás de escena y demás, ¿verdad?


Symbian tenía una AllocSize()función que devolvía el tamaño de una asignación con malloc(); esto solo funcionó para el puntero literal devuelto por malloc, y obtendría un galimatías o un bloqueo si le pedía que supiera el tamaño de un puntero no válido o un puntero desplazado de uno.

No quieres creer que no es posible, pero realmente no lo es. La única forma de saber la longitud de algo pasado a una función es realizar un seguimiento de la longitud usted mismo y pasarla como un parámetro explícito separado.

Will avatar Nov 25 '2011 12:11 Will