2009-12-10 13 views
5

He estado usando C++ por un momento ahora. Estoy nunca seguro de cómo funciona la gestión de memoria, así que aquí va:¿Cuándo se eliminan las variables de la memoria en C++?

estoy en primer lugar seguro de cómo la memoria está sin asignar en una función, por ejemplo:

int addTwo(int num) 
{ 
    int temp = 2; 
    num += temp; 
    return num; 
} 

lo tanto, en este ejemplo, ¿La temperatura se eliminaría de la memoria una vez que la función termine? Si no, ¿cómo se hace esto? En C# una variable se elimina una vez que se agote su alcance. ¿Hay algún otro caso que deba saber?

Gracias

+0

Reetiquetado como 'C' porque no hay C++ aquí, como lo señala alguien más a continuación. – ChrisInEdmonton

+5

Tetagged como C++, porque esta es definitivamente una pregunta de C++, y el interrogador pregunta explícitamente sobre C++. –

+8

OP, no es necesario que acepte ninguna de las respuestas. –

Respuesta

7

La variable local temp se "empujan" en una pila en el comienzo de la función y "estallar" de la pila cuando se cierra la función.

Aquí está un desmontaje de una versión no optimizada:

int addTwo(int num) 
{ 
00411380 push  ebp 
00411381 mov   ebp,esp    //Store current stack pointer 
00411383 sub   esp,0CCh   //Reserve space on stack for locals etc 
00411389 push  ebx 
0041138A push  esi 
0041138B push  edi 
0041138C lea   edi,[ebp-0CCh] 
00411392 mov   ecx,33h 
00411397 mov   eax,0CCCCCCCCh 
0041139C rep stos dword ptr es:[edi] 
    int temp = 2; 
0041139E mov   dword ptr [temp],2 
    num += temp; 
004113A5 mov   eax,dword ptr [num] 
004113A8 add   eax,dword ptr [temp] 
004113AB mov   dword ptr [num],eax 
    return num; 
004113AE mov   eax,dword ptr [num] 
} 
004113B1 pop   edi 
004113B2 pop   esi 
004113B3 pop   ebx 
004113B4 mov   esp,ebp     //Restore stack pointer 
004113B6 pop   ebp 
004113B7 ret   

Los términos "empujados" y "metimos" no son más que significaba como una analogía. Como se puede ver en el resultado del ensamblaje, el compilador reserva toda la memoria para las variables locales, etc. de una vez al restar un valor adecuado del puntero de la pila.

+3

No es exactamente cómo se usa la pila. La llamada de función asignará un marco en la pila, pero no se producirá ningún empuje ni estallido. –

+1

@Neil Lo sé, fue una analogía que sería más fácil de entender. Las comillas añadidas y algunas salidas de ensamblaje –

+2

push + pop son meramente conceptos: la temperatura segura se empuja/elimina de la pila. La implementación depende del compilador + de la arquitectura, y generalmente se puede esperar que todo el cuadro se "empuje" y "explote" como uno solo. Por razones históricas, el enfoque de la implementación de la pila es tal que la terminología utilizada difiere, pero todavía estamos hablando de colocar y eliminar datos de la estructura de datos último en entrar primero en salir: push y pop ciertamente no son términos * incorrectos * para eso. –

0

La temperatura variable se asigna a la pila. Eso significa que está desasignado cuando la función regresa.

Ver ej .:

+0

Re: C# eso no es cierto para todos los objetos, algunos son tipo de referencia (clases) y algunos son tipos de valor (estructuras, primativos, enumeraciones). –

+0

gracias por el comentario. Editado mi respuesta en consecuencia. – Sebastian

+0

¿Por qué los votos a favor? ¿Qué pasa con esta respuesta? –

11

Aquí, temp se asigna en la pila, y la memoria que se utiliza se libera automáticamente cuando se cierra la función. Sin embargo, usted podría asignarlo en el montón de la siguiente manera:

int *temp = new int(2); 

Para liberarla, que tiene que hacer

delete temp; 

Si asigna la variable en la pila, esto es lo que normalmente sucede:

Cuando llame a su función, incrementará esta cosa llamada 'puntero de la pila', un número que indica qué direcciones de la memoria deben 'protegerse' para ser utilizadas por sus variables locales. Cuando la función retorna, disminuirá el puntero de la pila a su valor original. En realidad, no se hace nada con las variables que ha asignado en esa función, excepto que la memoria en la que residen ya no está "protegida"; cualquier otra cosa puede (y finalmente lo hará) sobrescribirlas. Entonces se supone que no debes tener acceso a ellos por más tiempo.

Si necesita que la memoria asignada persista después de que haya salido de la función, utilice el montón.

+2

En ese ejemplo, la temperatura debería ser un puntero. –

+5

int temp = new int (2); es sintaxis incorrecta en C++ –

+0

whoops, fijo (15char) – int3

4

temp está asignado en la pila. Entonces cuando la función regresa, se va.

Las reglas de alcance de C++ son similares a C#.

+1

Para nitpick, yo diría que las reglas de C# scope son similares a C++ ya que C++ llegó antes de C#. –

+0

Similar-to es conmutativa, hasta donde yo sé. –

6

No se elimina de la memoria una vez que la función finaliza.

Permanece en la memoria, en el marco de la pila de Two, hasta que algún otro proceso (o el mismo) re use esa parte de la memoria.

Hasta ese momento, acceder a la temperatura es un comportamiento indefinido.

+4

Cierto, y esto puede ser importante para cierto trabajo. Sin embargo, para la mayoría de nosotros, conceptualmente la memoria utilizada por la temperatura ya no está disponible una vez que la función se completa. – tloach

+1

Esto nunca es un comportamiento relevante a menos que tengas un puntero a la temperatura. Y cuando lo haces, este comportamiento nunca es "importante para cierto trabajo", ya que es simplemente un error. Vale la pena ser consciente del hecho de que acceder a la memoria no asignada en la pila o en el montón puede dar como resultado un comportamiento indefinido (es decir, no se bloqueará necesariamente ni provocará un error). No veo cómo esto importa para cualquier funcionalidad que no tenga errores. –

+0

@eamon: Lo único que quise decir es que para cierto trabajo es importante tener en cuenta que el tamaño de su pila no se reducirá solo porque un grupo de variables locales quede fuera del alcance. Para las personas que trabajan en hardware donde cada byte importa ser conscientes de este comportamiento, puede informar una decisión de asignar espacio en la pila o en el montón. – tloach

0

En este caso, tanto num como temp son locales para esta función. Cuando se llama a la función, el número pasado a num se copia de la persona que llama a una variable en la pila. La temperatura se crea en la pila. Cuando regresa, el valor de num se copia nuevamente a la persona que llama, y ​​las variables temporales y numéricas utilizadas en la función se descartan.

+0

+1 para señalar que num también es local. – ChrisInEdmonton

0

En C, C++ variables locales tienen automática de clase de almacenamiento y se almacenan en Pila.

Cuando la función retorna, la pila se desenrolla y los locales ya no son accesibles, pero aún persisten en la memoria y esa es la razón por la que al definir una variable en la función puede contener valor basura.

Es solo la manipulación del puntero de la pila en la pila y en la memoria para el local se elimina en realidad.

+0

En realidad, la especificación C++ no menciona el almacenamiento de pila. La única mención es la duración de la variable. –

15

En C++ no es una regla muy simple de oro:

Toda la memoria se libera automáticamente cuando se ejecuta fuera de alcance a menos que se le ha asignado manualmente.

asignaciones manuales:

  • Cualquier objeto asignado por la nueva() deben estar sin asignar por una coincidencia de borrar().
  • Cualquier memoria asignada por malloc() DEBE ser desasignada por un coincidente libre().

Un patrón de diseño muy útil en C++ se llama RAII (Resource Acquisition Is Initialization) que se une asignaciones dinámicas a un objeto de ámbito que libera la asignación en su destructor.

En el código RAII ya no tiene que preocuparse por llamar a delete() o free() porque se llaman automáticamente cada vez que el "objeto de anclaje" se queda fuera del alcance.

1

Consulte mi respuesta a esta pregunta. Puede aclarar un montón de cosas para oyu.

How does automatic memory allocation actually work in C++?

No estoy publicación de un enlace por diversión. Mi respuesta es una mirada profunda (en un nivel muy introductorio) de cómo funciona la administración de memoria.

0

En C++, cualquier objeto que declare en cualquier ámbito se eliminará cuando se cierre el ámbito. En el ejemplo siguiente, se llama al constructor predeterminado cuando se declara el objeto y se invoca al destructor al salir, es decir, ~ MyClass.

void foo() { 
    MyClass object; 
    object.makeWonders(); 
} 

Si se declara un puntero en una función, entonces el puntero en sí (los 4 bytes para sistemas de 32 bits) obtiene reclamados cuando las salidas con ámbito, pero la memoria es posible que haya asignado el uso de operador nuevo o malloc perdurará - esto a menudo se conoce como pérdida de memoria.

void foo() { 
    MyClass* object = new MyClass; 
    object->makeWonders(); 
    // leaking sizeof(MyClass) bytes. 
} 

Si realmente debe asignar un objeto a través de nuevo que necesita ser eliminada cuando sale del alcance, entonces debería usar boost :: scoped_ptr así:

void foo() { 
    boost::scoped_ptr<MyClass> object(new MyClass); 
    object->makeWonders(); 
    // memory allocated by new gets automatically deleted here. 
} 
1

Normalmente Gestión de memoria se utiliza en el contexto de la memoria dinámica que se crea por

new 
malloc 

En el código C++ normales se comporta como cualquier otro idioma. Si crea una variable o la devuelve, se copia y se puede acceder desde el lado de destino.

int a = addTwo(3); 

obtiene una copia del valor devuelto. Si el valor devuelto es un operador de copia de clase llamado. Así que mientras no trabajes con new y malloc no tienes que preocuparte tanto por la administración de la memoria.

Una observación adicional que es importante

void func(std::string abc) 
{ 
    // method gets a copy of abc 
} 

void func(std::string& abc) 
{ 
    // method gets the original string object which can be modified without having to return it 
} 

void func(const std::string& abc) 
{ 
    // method gets the original string object abc but is not able to modify it  
} 

La diferencia de las tres líneas es muy importante debido a que su programa puede ahorrar mucho tiempo la creación de copias de los parámetros de entrada que normalmente no desea crear.

p. Ej.

bool CmpString(std::string a, std::string b) 
{ 
    return a.compare(b); 
} 

es muy caro porque las cuerdas a y b siempre se copian. Utilice

bool CmpString(const std::string& a, const std::string& b) 

en su lugar.

Esto es importante porque no se utilizan objetos refconados por defecto.

Cuestiones relacionadas