Pregunta ¿Qué sucede cuando una variable queda fuera del alcance?


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?


14
2017-12-15 00:53


origen


Respuestas:


El comportamiento real de su muestra de código está determinado por dos factores principales: 1) el comportamiento es indefinido 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 sucede físicamente, debe declarar sus indicadores 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.

Eso significa que la memoria anteriormente ocupada por tmp continúa siendo reservado 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 anteriormente ocupado por tmp, por lo que normalmente verá el valor 17 sobrevive intacto en ese recuerdo.

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 ha sido reutilizado para d y su valor anterior ha sido anulado. El segundo printf típicamente generará el mismo valor de puntero para ambos punteros.


15
2017-12-15 00:57



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".


5
2017-12-15 00:55



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.


3
2017-12-15 00:57