¿Cómo borra [] "sabe" el tamaño de la matriz de operandos?

250
Foo* set = new Foo[100];
// ...
delete [] set;

No pasas los límites de la matriz a delete[]. ¿Pero dónde se almacena esa información? ¿Está estandarizado?

VolkerK
fuente
sourceforge.net/projects/fastmm es de código abierto y reemplaza el administrador de memoria. Aquí puede averiguar cómo funciona la administración de memoria y de dónde provienen las informaciones para asignar y eliminar memorias.
1
Tenga en cuenta que FastMM es específico solo para los compiladores Delphi / C ++ Builder, no es un administrador de memoria de uso general para C ++. Ni siquiera está escrito en C ++.
Remy Lebeau el

Respuestas:

181

Cuando asigna memoria en el montón, su asignador realizará un seguimiento de la cantidad de memoria que ha asignado. Esto generalmente se almacena en un segmento "principal" justo antes de la memoria que se le asigna. De esa manera, cuando llega el momento de liberar la memoria, el desasignador sabe exactamente cuánta memoria liberar.

Peter Kühne
fuente
44
Tenga en cuenta que esto solo se aplica a las asignaciones de matrices en C ++. Todas las demás asignaciones dependen del tamaño del tipo. Algunas bibliotecas almacenan todos los tamaños de asignación, generalmente solo en modo de depuración.
Zan Lynx
97
No hay absolutamente ninguna razón por la cual esta información no debería estar disponible para el programador. Puedo pasar un puntero a una función y liberarlo, pero para obtener el tamaño yo mismo en esa misma función, tengo que pasar un parámetro adicional. ¿Eso tiene algún sentido?
Mark Ruzon
26
@ Mark, tiene un poco de sentido, porque en teoría libera al asignador para almacenar siempre el tamaño del bloque asignado (que puede diferir del tamaño del bloque solicitado ). Ciertos diseños de asignadores pueden necesitar esta información para sus propios fines, o pueden no ser lo suficientemente sofisticados como para usar información de tipo para rastrear el tamaño de las asignaciones de almacenamiento dinámico sin matriz, etc. Obligar al asignador a almacenar el tamaño solicitado (para que no necesitar pasar el tamaño de la matriz usted mismo) podría ser un obstáculo pequeño, pero podría tener un impacto en el rendimiento de los diseños de asignadores concebibles.
Doug McClean
33
Lo siento, pero esta respuesta pierde el punto. Lo que QuantumPete describió es básicamente "Cómo freesabe cuánta memoria desasignar". Sí, el tamaño del bloque de memoria se almacena "en algún lugar" malloc(normalmente en el bloque mismo), así que así es como se freesabe. Sin embargo, new[]/ delete[]es una historia diferente. Este último básicamente funciona sobre malloc/ free. new[]también almacena el número de elementos que creó en el bloque de memoria (independientemente de malloc), para que luego delete[]pueda recuperar y usar ese número para llamar al número correcto de destructores.
AnT
26
Es decir, físicamente se almacenan dos contadores en el bloque: tamaño de bloque (por malloc) y recuento de elementos (por new[]). Tenga en cuenta que el primero no se puede usar para calcular el segundo, ya que, en general, el tamaño del bloque de memoria puede ser mayor de lo realmente necesario para la matriz del tamaño solicitado. También tenga en cuenta que el contador de elementos de matriz solo es necesario para tipos con destructor no trivial. Para los tipos con destructor trivial, el contador no se almacena new[]y, por supuesto, no se recupera delete[].
AnT
23

UNO DE LOS enfoques para los compiladores es asignar un poco más de memoria y almacenar un recuento de elementos en un elemento principal.

Ejemplo de cómo se podría hacer:

aquí

int* i = new int[4];

el compilador asignará sizeof(int)*5bytes.

int *temp = malloc(sizeof(int)*5)

Almacenará "4" en los primeros sizeof(int)bytes

*temp = 4;

y establecer i

i = temp + 1;

Entonces iseñalará una matriz de 4 elementos, no 5.

Y eliminación

delete[] i;

se procesará de la siguiente manera:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
Avt
fuente
9

La información no está estandarizada. Sin embargo, en las plataformas en las que he trabajado, esta información se almacena en la memoria justo antes del primer elemento. Por lo tanto, teóricamente podrías acceder e inspeccionarlo, sin embargo, no vale la pena.

Además, esta es la razón por la que debe usar eliminar [] cuando asignó memoria con la nueva [], ya que la versión de matriz de eliminar sabe que (y dónde) debe buscar para liberar la cantidad correcta de memoria, y llamar al número apropiado de destructores para los objetos

Daemin
fuente
5

Básicamente se organiza en memoria como:

[info] [mem que pediste ...]

Donde información es la estructura utilizada por su compilador para almacenar la cantidad de memoria asignada, y qué no.

Sin embargo, esto depende de la implementación.

Francisco Soto
fuente
4

Esto no es algo que esté en la especificación, depende de la implementación.

jeffm
fuente
3

Está definido en el estándar C ++ para ser específico del compilador. Lo que significa magia de compilación. Se puede romper con restricciones de alineación no triviales en al menos una plataforma principal.

Puede pensar en posibles implementaciones al darse cuenta de que delete[]solo se define para los punteros devueltos por new[], que pueden no ser el mismo puntero devuelto por operator new[]. Una implementación en la naturaleza es almacenar el recuento de matrices en el primer int devuelto por operator new[]y new[]devolver un desplazamiento de puntero más allá de eso. (Es por eso que las alineaciones no triviales pueden romperse new[]).

Tenga en cuenta que operator new[]/operator delete[]! = new[]/delete[].

Además, esto es ortogonal a cómo C conoce el tamaño de la memoria asignada por malloc.

MSN
fuente
2

Porque la matriz que se 'eliminará' debería haberse creado con un solo uso del operador 'nuevo'. La 'nueva' operación debería haber puesto esa información en el montón. De lo contrario, ¿cómo podrían los usos adicionales de new saber dónde termina el montón?

Joel Coehoorn
fuente
0

No está estandarizado. En el tiempo de ejecución de Microsoft, el nuevo operador usa malloc () y el operador de eliminación usa free (). Entonces, en esta configuración, su pregunta es equivalente a lo siguiente: ¿Cómo sabe free () el tamaño del bloque?

Hay algo de contabilidad detrás de escena, es decir, en el tiempo de ejecución C.

Andre
fuente
55
No es verdad. Antes de llamar gratis, el delete [] necesita llamar primero a los destructores. Para esto, el tamaño total de las asignaciones no es suficiente. En realidad, new [] y delete [] funciona de manera diferente para tipos simples y destruidos en VC ++.
Suma
0

Este es un problema más interesante de lo que podrías pensar al principio. Esta respuesta es sobre una posible implementación.

En primer lugar, mientras que en algún nivel su sistema tiene que saber cómo 'liberar' el bloque de memoria, el malloc / free subyacente (que generalmente se llama a new / delete / new [] / delete []) no siempre recuerda exactamente cuánta memoria si lo solicita, se puede redondear (por ejemplo, una vez que está por encima de 4K, a menudo se redondea al siguiente bloque de tamaño 4K).

Por lo tanto, incluso si pudiera obtener el tamaño del bloque de memoria, eso no nos dice cuántos valores hay en la nueva memoria [] ed, ya que puede ser más pequeña. Por lo tanto, tenemos que almacenar un número entero adicional que nos dice cuántos valores hay.

EXCEPTO, si el tipo que se está construyendo no tiene un destructor, entonces eliminar [] no tiene que hacer nada excepto liberar el bloque de memoria, ¡y por lo tanto no tiene que almacenar nada!

Chris Jefferson
fuente