¿Hay alguna manera de determinar el tamaño de una matriz de C ++ programáticamente? Y si no, ¿por qué?

Esta pregunta se inspiró en una pregunta similar: ¿cómo elimina [] “saber” el tamaño de la matriz de operandos?

Mi pregunta es un poco diferente: ¿hay alguna manera de determinar el tamaño de una matriz de C ++ programáticamente? Y si no, ¿por qué? Cada función que he visto que toma una matriz también requiere un parámetro entero para darle el tamaño. Pero como señaló la pregunta vinculada, delete[] debe saber el tamaño de la memoria que se desasignará.

Considere este código de C ++:

 int* arr = new int[256]; printf("Size of arr: %d\n", sizeof(arr)); 

Esto imprime ” Size of arr: 4 “, que es solo el tamaño del puntero. Sería bueno tener alguna función que imprime 256, pero no creo que exista en C ++. (De nuevo, parte de la pregunta es por qué no existe).

Aclaración : sé que si declaro la matriz en la stack en lugar del montón (es decir, ” int arr[256]; “), el operador sizeof devolvería 1024 (longitud de matriz * sizeof (int)).

delete [] sabe el tamaño que se asignó. Sin embargo, ese conocimiento reside en el tiempo de ejecución o en el administrador de memoria del sistema operativo, lo que significa que no está disponible para el comstackdor durante la comstackción. Y sizeof() no es una función real, en realidad el comstackdor lo evalúa en una constante, algo que no puede hacer para las matrices asignadas dinámicamente, cuyo tamaño no se conoce durante la comstackción.

Además, considere este ejemplo:

 int *arr = new int[256]; int *p = &arr[100]; printf("Size: %d\n", sizeof(p)); 

¿Cómo sabría el comstackdor cuál es el tamaño de p ? La raíz del problema es que las matrices en C y C ++ no son objetos de primera clase. Se descomponen en punteros, y no hay forma de que el comstackdor o el progtwig en sí sepa si un puntero apunta al comienzo de un trozo de memoria asignado por new , o a un solo objeto, o a algún lugar en el medio de un pedazo de memoria asignado por new .

Una razón para esto es que C y C ++ dejan la administración de memoria al progtwigdor y al sistema operativo, que es también la razón por la cual no tienen recolección de basura. La implementación de new y delete no es parte del estándar de C ++, porque C ++ está destinado a ser utilizado en una variedad de plataformas, que pueden administrar su memoria de maneras muy diferentes. Es posible permitir que C ++ realice un seguimiento de todas las matrices asignadas y sus tamaños si está escribiendo un procesador de textos para un cuadro de Windows que se ejecuta en la última CPU Intel, pero puede ser completamente inviable cuando está escribiendo un sistema integrado que se ejecuta un DSP.

No, no hay forma de hacerlo en Standard C ++.

No hay realmente una buena razón por la que no estoy enterado. Probablemente, el tamaño se consideró un detalle de implementación y mejor no expuesto. Tenga en cuenta que cuando dice malloc (1000), no hay garantía de que el bloque devuelto sea de 1000 bytes, solo que tiene al menos 1000 bytes. Lo más probable es que sea alrededor de 1020 (1 K menos 4 bytes para gastos generales). En ese caso, el tamaño “1020” es el más importante para recordar en la biblioteca en tiempo de ejecución. Y, por supuesto, eso cambiaría entre implementaciones.

Por eso, el comité de estándares agregó std: vector <>, que sí hace un seguimiento de su tamaño exacto.

Bueno, en realidad, hay una manera de determinar el tamaño, pero no es “seguro” y será diferente del comstackdor al comstackdor … por lo que no debería usarse en absoluto .

Cuando lo hace: int * arr = new int [256];

El 256 es irrelevante, se le dará 256 * sizeof (int) suponiendo que para este caso 1024, este valor se almacenará probablemente en (arr – 4)

Entonces para darle la cantidad de “artículos”

int * p_iToSize = arr – 4;

printf (“Número de elementos% d”, * p_iToSize / sizeof (int));

Por cada malloc, nuevo, lo que sea que esté antes del bloque de memoria continua que recibe, también se le asigna un espacio reservado con cierta información con respecto al bloque de memoria que le fue entregado.

Una forma común de manejar esto es usar un vector

 int main() { std::vector v(256); printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity()); } 

o predefinir el tamaño

 const int arrSize = 256; int main() { int array[arrSize]; printf("Size of array is %i", sizeof(int) * arrSize); } 

C ++ decidió añadir algo nuevo para hacer un malloc tipo seguro, que nuevo debe conocer los números de los elementos e para llamar a los ctors, así que elimínelos para llamar a los dtors. En los primeros días tienes que pasar para borrar los números de objetos que pasaste a nuevos.

 string* p = new string[5]; delete[5] p; 

Sin embargo, pensaron que si usaban [] nuevo, la sobrecarga de un número era pequeña. Entonces decidieron que new [n] debe recordar ny pasarlo para borrar. Hay tres formas principales de implementarlo.

  1. mantener una tabla hash de puntero a medida
  2. lo escribió directamente cerca del vector
  3. hacer algo completamente diferente

Tal vez es posible obtener el tamaño así:

 size_t* p = new size_t[10]; cout < < p[-1] << endl; // Or cout << p[11] << endl; 

O el infierno ninguno de esos.

Dependiendo de su aplicación, puede crear un “valor centinela” al final de su matriz.

El valor centinela debe tener alguna propiedad única.

Luego puede procesar la matriz (o hacer una búsqueda lineal) para el valor centinela, contando a medida que avanza. Una vez que alcanzas el valor de centinela, tienes tu cuenta de matriz.

Para una cadena C simple, la terminación \ 0 es un ejemplo de un valor centinela.

Algo de magia:

 template  inline size_t array_size(const T (&v)[S]) { return S; } 

Y así es como lo hacemos en C ++ 11:

 template constexpr auto array_size(const T (&)[S]) -> size_t { return S; } 

Eso es porque tu variable arr es solo un puntero. Mantiene la dirección de una ubicación particular en la memoria, sin saber nada al respecto. Usted declara que es int *, lo que le da al comstackdor alguna indicación de qué hacer cuando incrementa el puntero. Aparte de eso, podría estar apuntando al principio o al final de la matriz o en la stack o en la memoria no válida. Pero estoy de acuerdo contigo, no poder llamar a sizeof es muy molesto 🙂

QuantumPete

No existe una forma portátil de determinar el tamaño de una matriz asignada dinámicamente en C ++ dado solo su puntero. C ++ está hecho para ser muy flexible y para darle poder al usuario. Por ejemplo, el estándar no define cómo deben funcionar los asignadores de memoria, por ejemplo, agregando un encabezado de tamaño requerido. No requerir un encabezado permite mucha más flexibilidad.

Como un ejemplo, considere una cadena implementada como una matriz char *. Es común utilizar punteros en el medio de la matriz para seleccionar subcadenas. Como ejemplo, vea la función strtok en la biblioteca C estándar. Si se requiere incrustar algún encabezado justo antes de cada matriz, tendrá que eliminar porciones de la matriz antes de la subcadena.

Una forma alternativa de manejar los encabezados sería tener encabezados de matriz en un bloque de memoria y hacer que apunten a la memoria de matriz cruda en otro lugar. En muchas situaciones, esto requeriría dos búsquedas de punteros para cada referencia, lo que supondría un gran lastre para el rendimiento. Existen formas de superar estas deficiencias, pero agregan complejidad y reducen la flexibilidad de implementación.

La plantilla std :: vector es mi forma favorita de mantener el tamaño de una matriz vinculada a la matriz en sí.

C es un lenguaje ensamblador portátil con una mejor syntax.

Desafortunadamente, esto no es posible. En C y C ++, es responsabilidad del progtwigdor recordar la longitud de una matriz, ya que la longitud de la matriz no se almacena en ninguna parte. Delete [] y free () recuerdan el tamaño del bloque asignado pero pueden asignar más memoria que la solicitada para que sus estructuras internas de datos que almacenan los tamaños de bloques de memoria asignados no le den el tamaño exacto de su matriz.

Tenga en cuenta que los vectores C ++ STL, que básicamente son matrices envueltas en una clase con algunas funciones auxiliares, almacenan la longitud de la matriz, por lo que si realmente necesita esta funcionalidad, puede usar vectores.

En general, no. Las matrices en C y C ++ son solo bloques de memoria sin información de contabilidad adjunta. Sin almacenar la longitud de la matriz en la memoria y agregar sobrecarga para hacerlo, es imposible en el caso general.

Existe una excepción para las matrices que están asignadas estáticamente. Por ejemplo, si declara: int a[50] entonces sizeof(a) funcionará. Esto es posible porque [50] es parte del tipo estático de la matriz: es conocido por el comstackdor. sizeof se interpreta en tiempo de comstackción.

Sin embargo, si creas un puntero: int *p = a , entonces sizeof(p) devolverá el tamaño del puntero como mencionas, no el tamaño de la matriz, porque el comstackdor no sabe a qué p apunta.

No puedes, fundamentalmente:

 void foo(int* arr); int arr[100] = {0}; foo(arr+1); // Calls foo with a pointer to 100-1 elements. 

Una matriz C ++ no es más que una colección de objetos que se almacenan en una región de memoria contigua. Como no hay agujeros entre ellos (el relleno está dentro de los objetos), puede encontrar el siguiente elemento de una matriz simplemente ingresando el puntero. En el nivel de la CPU, este es un ajuste simple. C ++ solo inserta un multiplicador de sizeof (elemento).

Tenga en cuenta que las implementaciones pueden optar por implementar “punteros adictivos” que contienen límites de matriz. Tendrían que ser dos veces más grandes, ya que necesitarían vincularse a algún tipo de “descriptor de matrices enlazadas”. Como efecto secundario, en tales implementaciones podría llamar a delete [] (1+new int[5]);

No, no hay forma de hacer esto, tienes que hacer un seguimiento de lo grande que es externamente. Las clases como std::vector hacen esto por ti.

Ahora existe std :: array , un contenedor de tiempo de comstackción eficiente alrededor de una matriz de tamaño constante:

 #include  int main (int argc, char** argv) { std::array arr; printf("Size of arr: %ld\n", arr.size()); } 

Los parámetros son .

También obtienes algunas otras sutilezas, como iteradores, empty () y max_size ().

El comstackdor no puede saber eso

 char *ar = new char[100] 

es una matriz de 100 caracteres porque no crea una matriz real en la memoria, solo crea un puntero a 100 bytes no inicializados en la memoria.

Si quiere saber el tamaño de la matriz dada, simplemente use std :: vector. std :: vector es una mejor matriz simplemente.

¿Hay alguna manera de determinar el tamaño de una matriz de C ++ programáticamente? Y si no, ¿por qué?

  1. No, a menos que lo sigas tú mismo.
  2. Porque si el comstackdor no tiene que contarle a nadie más que a sí mismo acerca de esa información, limita al comstackdor menos. Si eso es deseable o no es tema de debate.

@Dima,

¿Cómo sabría el comstackdor cuál es el tamaño de p?

El comstackdor debe saber el tamaño de p; de lo contrario, no puede implementar delete[] . El comstackdor no necesita contarle a nadie cómo se da cuenta.

Para una forma divertida de verificar esto, compare el puntero devuelto por el operator new[] con el puntero devuelto por el new[] .

Cuando crea apuntadores de matriz (Crear envoltorio con plantilla para punteros) no puede hacerlo, pero cuando crea una matriz de objetos, puede obtener el tamaño de la matriz de esa manera:

 char* chars=new char[100]; printf("%d",*((int*)chars-1)); 

La función delete[] necesita deconstruir todos los objetos en ella. para hacerlo, la new[] palabra clave new[] pone la cantidad de elementos detrás de toda la matriz.

El cuerpo de la matriz es así:

 int count; ObjectType* data; //This value is returned when using new[] 

la forma en que lo hago es dividiendo el tamaño de la matriz por el tamaño del primer elemento

 int intarray[100]; printf ("Size of the array %d\n", (sizeof(intarray) / sizeof(intarray[0])); 

Imprime 100

Simplemente podría crear un elemento adicional de la matriz y luego aplicar el número más improbable que se almacenará en la matriz. Luego puede determinar el número de elementos mediante alguna función pasando ese número.

En el caso de declarar e inicializar una matriz en el momento de la creación, puede escanearla y luego generar un número que no coincida con ninguno de los elementos de la matriz. Pero si luego modifica uno de los elementos, no sabrá si ese elemento almacena el mismo valor que el último elemento, por lo que tendrá que generar un nuevo número para almacenar en el último elemento. Repasando todo eso, usted también podría almacenar la cantidad total de elementos en el momento de la creación en una variable. Y ese será probablemente el caso si solo usa la matriz dentro de una función.