program - passing arguments in c
¿Qué sucede cuando una variable queda fuera del alcance? (3)
En la mayoría de los lenguajes administrados (es decir, los que tienen un GC), las variables locales que salen del alcance son inaccesibles y tienen una prioridad GC más alta (por lo tanto, se liberarán primero).
Ahora, C no es un lenguaje administrado, ¿qué sucede con las variables que están fuera del alcance aquí?
Creé un pequeño caso de prueba en C:
#include <stdio.h>
int main(void){
int *ptr;
{
// New scope
int tmp = 17;
ptr = &tmp; // Just to see if the memory is cleared
}
//printf("tmp = %d", tmp); // Compile-time error (as expected)
printf("ptr = %d/n", *ptr);
return 0;
}
Estoy usando GCC 4.7.3 para compilar y el programa de arriba imprime 17
, ¿por qué? ¿Y cuándo / en qué circunstancias se liberarán las variables locales?
El comportamiento real de su muestra de código está determinado por dos factores principales: 1) el comportamiento no está definido por el idioma, 2) un compilador de optimización generará código de máquina que no coincide físicamente con su código C.
Por ejemplo, a pesar de que el comportamiento no está definido, GCC puede (y lo hará) optimizar fácilmente su código a un mero
printf("ptr = %d/n", 17);
lo que significa que la salida que ve tiene muy poco que ver con lo que le sucede a cualquier variable en su código.
Si desea que el comportamiento de su código refleje mejor lo que ocurre físicamente, debe declarar que sus punteros son volatile
. El comportamiento aún no estará definido, pero al menos restringirá algunas optimizaciones.
Ahora, en cuanto a qué sucede con las variables locales cuando salen del alcance. Nada físico sucede. Una implementación típica asignará suficiente espacio en la pila de programas para almacenar todas las variables en el nivel más profundo de anidación de bloques en la función actual. Este espacio normalmente se asigna en la pila de una vez al inicio de la función y se devuelve a la salida de la función.
Esto significa que la memoria ocupada anteriormente por tmp
continúa siendo reservada en la pila hasta que la función finalice. Eso también significa que el mismo espacio de pila puede (y será) reutilizado por diferentes variables que tengan aproximadamente el mismo nivel de "profundidad de localidad" en bloques hermanos. El espacio mantendrá el valor de la última variable hasta que alguna otra variable declarada en alguna variable del bloque hermano lo anule. En su ejemplo, nadie anula el espacio ocupado anteriormente por tmp
, por lo que normalmente verá que el valor 17
sobrevive intacto en esa memoria.
Sin embargo, si haces esto
int main(void) {
volatile int *ptr;
volatile int *ptrd;
{ // Block
int tmp = 17;
ptr = &tmp; // Just to see if the memory is cleared
}
{ // Sibling block
int d = 5;
ptrd = &d;
}
printf("ptr = %d %d/n", *ptr, *ptrd);
printf("%p %p/n", ptr, ptrd);
}
Verá que el espacio anteriormente ocupado por tmp
se ha reutilizado para d
y se ha anulado su valor anterior. El segundo printf
típicamente generará el mismo valor de puntero para ambos punteros.
La vida útil de un objeto automático finaliza al final del bloque donde se declara.
El acceso a un objeto fuera de su tiempo de vida es un comportamiento indefinido en C.
(C99, 6.2.4p2) "Si se hace referencia a un objeto fuera de su tiempo de vida, el comportamiento no está definido. El valor de un puntero se vuelve indeterminado cuando el objeto al que apunta llega al final de su vida útil".
Las variables locales se asignan en la pila. No se "liberan" en el sentido en que piensas en los lenguajes de GC, o en la memoria asignada en el montón. Simplemente salen del alcance, y para los tipos integrados el código no hará nada, y para los objetos, se llama al destructor.
Acceder a ellos más allá de su alcance es un comportamiento indefinido. Tuviste suerte, ya que ningún otro código ha sobreescrito ese área de memoria ... todavía.