new allocate c++ memory allocation memory-management

c++ - allocate - Asignación de memoria/desasignación?



dynamic memory c++ new (4)

1. ¿Dónde está la "memoria" que se está asignando?

Desde una perspectiva de lenguaje, esto no se especifica, y sobre todo porque los detalles finos a menudo no importan. Además, el estándar de C++ tiende a errar al lado de los detalles de hardware que no se especifican lo suficiente, para minimizar las restricciones innecesarias (tanto en las plataformas en que los compiladores pueden ejecutarse, como en las posibles optimizaciones).

La respuesta de sftrabbit ofrece una gran visión general de este extremo de las cosas (y es todo lo que realmente necesitas), pero puedo darte un par de ejemplos trabajados en caso de que eso ayude.

Ejemplo 1:

En una computadora de un solo usuario suficientemente antigua (o una suficientemente pequeña), la mayor parte de la RAM física puede estar directamente disponible para su programa. En este escenario, llamar a malloc o new es esencialmente una contabilidad interna, lo que permite a la biblioteca de tiempo de ejecución rastrear qué fragmentos de esa RAM están actualmente en uso. Puedes hacerlo manualmente, pero se vuelve tedioso bastante rápido.

Ejemplo 2:

En un sistema operativo multitarea moderno, la memoria RAM física se comparte con muchos procesos y otras tareas, incluidos los hilos del kernel. También se utiliza para el almacenamiento en caché de disco y el almacenamiento en memoria intermedia de E / S en segundo plano, y se complementa con el subsistema de memoria virtual que puede intercambiar datos al disco (u otro dispositivo de almacenamiento) cuando no se están utilizando.

En este caso, llamar a new primero puede verificar si su proceso ya tiene suficiente espacio libre internamente y, si no, solicitar más del sistema operativo. Cualquier memoria que se devuelva puede ser física, o puede ser virtual (en cuyo caso, la memoria RAM física no se puede asignar para almacenarla hasta que realmente se acceda). Ni siquiera puede notar la diferencia, al menos sin utilizar API específicas de la plataforma, porque el hardware de la memoria y el kernel conspiran para ocultarlo.

2. ¿Qué es este "recuerdo"? Espacio en una matriz? ¿O algo mas?

En el ejemplo 1, es algo así como espacio en una matriz: la dirección devuelta identifica un fragmento direccionable de RAM física. Incluso aquí, las direcciones de RAM no son necesariamente planas o contiguas: algunas direcciones pueden reservarse para ROM o para puertos de E / S.

En el ejemplo 2, es un índice en algo más virtual: el espacio de direcciones de su proceso. Esta es una abstracción utilizada para ocultar los detalles de la memoria virtual subyacente de su proceso. Cuando acceda a esta dirección, el hardware de la memoria puede acceder directamente a alguna RAM real, o podría necesitar solicitar al subsistema de memoria virtual que proporcione algunos.

3. ¿Qué sucede exactamente cuando se asigna esta "memoria"?

En general, se devuelve un puntero que puede usar para almacenar tantos bytes como usted solicitó. En ambos casos, malloc o el new operador realizarán algunas tareas de mantenimiento para rastrear qué partes del espacio de direcciones de su proceso se utilizan y cuáles son gratuitas.

4. ¿Qué sucede exactamente cuando la memoria se desasigna?

Nuevamente, en general, free o delete harán algunas tareas domésticas para que sepan que la memoria está disponible para ser reasignada.

También me ayudaría realmente si alguien pudiera responder a lo que malloc hace en estas líneas de C ++:

char* x; x = (char*) malloc (8);

Devuelve un puntero que es NULL (si no pudo encontrar los 8 bytes que desea), o algún valor no NULL.

Lo único que puedes decir acerca de este valor no nulo es que:

  • es legal (y seguro) acceder a cada uno de esos 8 bytes x[0]..x[7] ,
  • es ilegal (comportamiento indefinido) acceder a x[-1] o x[8] o en realidad a cualquier x[i] menos que 0 <= i <= 7
  • es legal comparar cualquiera de x, x+1, ..., x+8 (aunque no se puede desreferenciar a los últimos)
  • si su plataforma / hardware / lo que sea tiene alguna restricción sobre dónde puede almacenar datos en la memoria, entonces x cumple

He estado buscando asignación de memoria últimamente y estoy un poco confundido sobre los conceptos básicos. No he podido entender lo simple. ¿Qué significa asignar memoria? ¿Lo que pasa? Apreciaría las respuestas a cualquiera de estas preguntas:

  1. ¿Dónde está la "memoria" que se está asignando?
  2. ¿Qué es este "recuerdo"? Espacio en una matriz? ¿O algo mas?
  3. ¿Qué sucede exactamente cuando se asigna esta "memoria"?
  4. ¿Qué sucede exactamente cuando la memoria se desasigna?
  5. También me ayudaría realmente si alguien pudiera responder a lo que malloc hace en estas líneas de C ++:

    char* x; x = (char*) malloc (8);

Gracias.


El modelo de memoria

El estándar C ++ tiene un modelo de memoria . Intenta modelar la memoria en un sistema informático de forma genérica. El estándar define que un byte es una unidad de almacenamiento en el modelo de memoria y que la memoria está compuesta de bytes (§1.7):

La unidad de almacenamiento fundamental en el modelo de memoria C ++ es el byte. [...] La memoria disponible para un programa C ++ consiste en una o más secuencias de bytes contiguos.

El modelo de objetos

El estándar siempre proporciona un modelo de objetos . Esto especifica que un objeto es una región de almacenamiento (por lo tanto, está compuesto por bytes y reside en la memoria) (§1.8):

Las construcciones en un programa C ++ crean, destruyen, refieren, acceden y manipulan objetos. Un objeto es una región de almacenamiento.

Entonces ahí vamos La memoria es donde se almacenan los objetos. Para almacenar un objeto en la memoria, debe asignarse la región de almacenamiento requerida.

Funciones de Asignación y Desasignación

El estándar proporciona dos funciones de asignación de alcance global implícitamente declaradas:

void* operator new(std::size_t); void* operator new[](std::size_t);

Cómo se implementan estos no es la preocupación de la norma. Lo único que importa es que deben devolver un puntero a alguna región de almacenamiento con el número de bytes correspondiente al argumento pasado (§3.7.4.1):

La función de asignación intenta asignar la cantidad de almacenamiento solicitada. Si tiene éxito, devolverá la dirección del inicio de un bloque de almacenamiento cuya longitud en bytes será al menos tan grande como el tamaño solicitado. No hay restricciones en el contenido del almacenamiento asignado a cambio de la función de asignación.

También define dos funciones de desasignación correspondientes:

void operator delete(void*); void operator delete[](void*);

Que se definen para desasignar el almacenamiento que se ha asignado previamente (§3.7.4.2):

Si el argumento dado a una función de desasignación en la biblioteca estándar es un puntero que no es el valor del puntero nulo (4.10), la función de desasignación desasignará el almacenamiento al que hace referencia el puntero, invalidando todos los punteros en referencia a cualquier parte del almacenamiento desasignado .

new y delete

Por lo general, no debería necesitar utilizar las funciones de asignación y desasignación directamente porque solo le proporcionan una memoria no inicializada. En cambio, en C ++ debe usar new y delete para asignar objetos dinámicamente. Una nueva expresión obtiene almacenamiento para el tipo solicitado mediante el uso de una de las funciones de asignación anteriores y luego inicializa ese objeto de alguna manera. Por ejemplo, new int() asignará espacio para un objeto int y luego lo inicializará a 0. Ver §5.3.4:

Una nueva expresión obtiene almacenamiento para el objeto llamando a una función de asignación (3.7.4.1).

[...]

Una nueva expresión que crea un objeto de tipo T inicializa ese objeto [...]

En la dirección opuesta, delete llamará al destructor de un objeto (si hay alguno) y luego desasignará el almacenamiento (§5.3.5):

Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación invocará el destructor (si lo hay) para el objeto o los elementos de la matriz que se eliminan.

[...]

Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación llamará a una función de desasignación (3.7.4.2).

Otras asignaciones

Sin embargo, estas no son las únicas formas en que se asigna o desasigna el almacenamiento. Muchos constructos del lenguaje implícitamente requieren asignación de almacenamiento. Por ejemplo, dando una definición de objeto, como int a; , también requiere almacenamiento (§7):

Una definición hace que se reserve la cantidad apropiada de almacenamiento y que se realice cualquier inicialización apropiada (8.5).

Biblioteca estándar C: malloc y free

Además, el encabezado <cstdlib> trae el contenido de la biblioteca estándar stdlib.h C, que incluye las funciones malloc y free . También están definidos, por el estándar C, para asignar y desasignar memoria, al igual que las funciones de asignación y desasignación definidas por el estándar C ++. Aquí está la definición de malloc (C99 §7.20.3.3):

void *malloc(size_t size);
Descripción
La función malloc asigna espacio para un objeto cuyo tamaño se especifica por size y cuyo valor es indeterminado.
Devoluciones
La función malloc devuelve un puntero nulo o un puntero al espacio asignado.

Y la definición de free (C99 §7.20.3.2):

void free(void *ptr);
Descripción
La función free hace que el espacio apuntado por ptr sea ​​desasignado, es decir, que esté disponible para una asignación posterior. Si ptr es un puntero nulo, no se produce ninguna acción. De lo contrario, si el argumento no coincide con un puntero devuelto anteriormente por la función calloc , malloc o realloc , o si el espacio ha sido desasignado por una llamada a free o realloc , el comportamiento no está definido.

Sin embargo, nunca hay una buena excusa para usar malloc y free en C ++. Como se describió anteriormente, C ++ tiene sus propias alternativas.

Respuestas a las preguntas

Entonces, para responder sus preguntas directamente:

  1. ¿Dónde está la "memoria" que se está asignando?

    El estándar C ++ no le importa. Simplemente dice que el programa tiene algo de memoria que está compuesta de bytes. Esta memoria se puede asignar.

  2. ¿Qué es este "recuerdo"? Espacio en una matriz? ¿O algo mas?

    En lo que respecta al estándar, la memoria es solo una secuencia de bytes. Esto es intencionalmente muy genérico, ya que el estándar solo trata de modelar sistemas informáticos típicos. Puedes, en su mayor parte, pensar en ello como un modelo de la memoria RAM de tu computadora.

  3. ¿Qué sucede exactamente cuando se asigna esta "memoria"?

    La asignación de memoria hace que una cierta área de almacenamiento esté disponible para el uso del programa. Los objetos se inicializan en la memoria asignada. Todo lo que necesita saber es que puede asignar memoria. La asignación real de memoria física a su proceso tiende a ser realizada por el sistema operativo.

  4. ¿Qué sucede exactamente cuando la memoria se desasigna?

    La desasignación de memoria previamente asignada ocasiona que la memoria no esté disponible para el programa. Se convierte en almacenamiento desasignado.

  5. También me ayudaría realmente si alguien pudiera responder a lo que malloc hace en estas líneas de C ++:

    char* x; x = (char*) malloc (8);

    Aquí, malloc simplemente asigna 8 bytes de memoria. El puntero que devuelve se convierte en un char* y se almacena en x .


Asignar memoria significa pedirle memoria al sistema operativo. Significa que es el programa en sí mismo el que solicita "espacio" en la RAM cuando solo lo necesita. Por ejemplo, si desea utilizar una matriz pero no conoce su tamaño antes de que se ejecute el programa, puede hacer dos cosas: - declarar y ordenar [x] con x dediced por usted, arbitrariamente largo. Por ejemplo, 100. ¿Pero qué pasa si su programa solo necesita una matriz de 20 elementos? Estás desperdiciando memoria por nada. - entonces tu programa puede malloc una matriz de x elementos justo cuando conoce el tamaño correcto de x. Los programas en la memoria se dividen en 4 segmentos: -código de pila (necesario para llamar a las funciones) (el código ejecutable de bibary) - datos (variables / datos globales) - montón, en este segmento se encuentra la memoria asignada. Cuando decide que ya no necesita la memoria asignada, la devuelve al sistema operativo.

Si quieres alloc y array de 10 enteros, haces:

int * array = (int *) malloc (sizeof (int) * 10)

Y luego se lo devuelve al sistema operativo con free (array)


1) ¿Dónde está la "memoria" que se está asignando?

Esto es completamente diferente en función de su sistema operativo, entorno de programación (gcc vs Visual C ++ vs Borland C ++ vs cualquier otra cosa), computadora, memoria disponible, etc. En general, la memoria se asigna de lo que se llama el montón, región de memoria esperando alrededor para que lo use. Por lo general, usará su RAM disponible. Pero siempre hay excepciones. En su mayor parte, siempre que nos dé memoria, de dónde viene no es una gran preocupación. Hay tipos especiales de memoria, como la memoria virtual, que pueden o no estar en la memoria RAM en cualquier momento dado y pueden moverse a su disco duro (o dispositivo de almacenamiento similar) si se queda sin memoria. ¡Una explicación completa sería muy larga!

2) ¿Qué es esta "memoria"? Espacio en una matriz? ¿O algo mas?

La memoria generalmente es la RAM en su computadora. Si es útil pensar en la memoria como una "matriz" gigantesca, con certeza funciona como tal, entonces piense en ello como una tonelada de bytes (valores de 8 bits, muy parecidos a los valores de caracteres unsigned char ). Comienza en un índice de 0 en la parte inferior de la memoria. Sin embargo, al igual que antes, hay toneladas de excepciones aquí y algunas partes de la memoria pueden asignarse al hardware, ¡o puede que ni siquiera existan!

3) ¿Qué sucede exactamente cuando se asigna esta "memoria"?

En un momento dado debería haber (¡realmente esperamos!) Algunos de ellos disponibles para asignar el software. Cómo se asigna es altamente dependiente del sistema. En general, se asigna una región de memoria, el asignador la marca como se usa, y luego se le da un puntero para usar que le dice al programa en qué parte de la memoria de su sistema está ubicada esa memoria. En su ejemplo, el programa encontrará un bloque consecutivo de 8 bytes (char) y devolverá un puntero al lugar donde encontró ese bloque después de marcarlo como "en uso".

4) ¿Qué sucede exactamente cuando la memoria se desasigna?

El sistema marca esa memoria como disponible para su uso nuevamente. Esto es increíblemente complicado porque esto a menudo causará agujeros en la memoria. Asigne 8 bytes y luego 8 bytes más, luego desasigne los primeros 8 bytes y obtendrá un agujero. Hay libros enteros escritos sobre el manejo de la desasignación, la asignación de memoria, etc. ¡Así que espero que la breve respuesta sea suficiente!

5) También me ayudaría realmente si alguien pudiera responder a lo que malloc hace en estas líneas de C ++:

REALMENTE crudamente, y suponiendo que está en una función (por cierto, nunca hagas esto porque no desasigna tu memoria y causa una pérdida de memoria):

void mysample() { char *x; // 1 x = (char *) malloc(8); // 2 }

1) Este es un puntero reservado en el espacio de pila local. No se ha inicializado, por lo que apunta a lo que ese bit de memoria tenía.

2) Llama a malloc con un parámetro de 8. El reparto solo permite que C / C ++ sepa que intentas que sea a (char *) porque devuelve a (void *) lo que significa que no tiene ningún tipo aplicado. Luego, el puntero resultante se almacena en su variable x.

En un ensamblaje muy burdo x86 de 32 bits, esto se verá vagamente como

PROC mysample: ; char *x; x = DWord Ptr [ebp - 4] enter 4, 0 ; Enter and preserve 4 bytes for use with ; x = (char *) malloc(8); push 8 ; We''re using 8 for Malloc call malloc ; Call malloc to do it''s thing sub esp, 4 ; Correct the stack mov x, eax ; Store the return value, which is in EAX, into x leave ret

La asignación real se describe vagamente en el punto 3. Malloc por lo general solo llama a una función del sistema para esto que maneja todo el resto, y como todo lo demás aquí, es muy diferente de sistema operativo a sistema operativo, sistema a sistema, etc.