¿Cuándo se asignan/inicializan las variables estáticas a nivel de función?

StackOverflow https://stackoverflow.com/questions/55510

  •  09-06-2019
  •  | 
  •  

Pregunta

Estoy bastante seguro de que las variables declaradas globalmente se asignan (e inicializan, si corresponde) en el momento de inicio del programa.

int globalgarbage;
unsigned int anumber = 42;

Pero ¿qué pasa con los estáticos definidos dentro de una función?

void doSomething()
{
  static bool globalish = true;
  // ...
}

¿Cuándo es el espacio para globalish ¿asignado?Supongo que cuando comienza el programa.¿Pero entonces también se inicializa?¿O se inicializa cuando doSomething() se llama primero?

¿Fue útil?

Solución

Tenía curiosidad sobre esto, así que escribí el siguiente programa de prueba y lo compilé con g++ versión 4.1.2.

include <iostream>
#include <string>

using namespace std;

class test
{
public:
        test(const char *name)
                : _name(name)
        {
                cout << _name << " created" << endl;
        }

        ~test()
        {
                cout << _name << " destroyed" << endl;
        }

        string _name;
};

test t("global variable");

void f()
{
        static test t("static variable");

        test t2("Local variable");

        cout << "Function executed" << endl;
}


int main()
{
        test t("local to main");

        cout << "Program start" << endl;

        f();

        cout << "Program end" << endl;
        return 0;
}

Los resultados no fueron los que esperaba.El constructor del objeto estático no se llamó hasta la primera vez que se llamó a la función.Aquí está el resultado:

global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed

Otros consejos

Alguna palabrería relevante del estándar C++:

3.6.2 Inicialización de objetos no locales [basic.start.init]

1

El almacenamiento de objetos con duración de almacenamiento estático (básico.stc.estático) se inicializará a cero (dcl.init) antes de que tenga lugar otra inicialización.Objetos de tipos de podTipos basicos) con duración de almacenamiento estático inicializada con expresiones constantes (expr.const) se sustituirán por: inicializado antes de cualquier dinámica inicialización se lleva a cabo.Objetos de ámbito de espacio de nombres con duración de almacenamiento estática definida en la misma unidad de traducción y se inicializará dinámicamente inicializados en el orden en que su definición aparece en la unidad de traducción.[Nota: dcl.init.aggr describe la orden en el que se inicializan los miembros agregados.El inicialización de objetos estáticos locales se describe en stmt.dcl. ]

[más texto a continuación agregando más libertades para los escritores de compiladores]

6.7 Declaración de declaración [stmt.dcl]

...

4

La inicialización cero (dcl.init) de todos los objetos locales con almacenamiento estático duración (básico.stc.estático) se realiza antes de cualquier otra inicialización tiene lugar.Un objeto local de Tipo de POD (Tipos basicos) con estática almacenamiento duración inicializado con expresiones constantes se inicializa antes de su bloque se introduce primero.Una implementación está permitido realizar inicialización temprana de otros objetos locales con almacenamiento estático duración en las mismas condiciones que una aplicación permitido inicializar estáticamente un objeto con almacenamiento estático duración en el ámbito del espacio de nombres (inicio.inicio.básico).De lo contrario, tales un objeto se inicializa la primera vez que el control pasa por su declaración;tal objeto se considera inicializado en el finalización de su inicialización.Si la inicialización sale por lanzando una excepción, la inicialización no está completa, por lo que se intentará de nuevo la próxima vez que el control entre en la declaración.Si el control vuelve a entrar en la declaración (recursivamente) mientras el objeto está siendo inicializado, el comportamiento es indefinido.[Ejemplo:

      int foo(int i)
      {
          static int s = foo(2*i);  // recursive call - undefined
          return i+1;
      }

--ejemplo final]

5

El destructor para un objeto local con duración de almacenamiento estático se ejecutará si y solo si se construyó la variable.[Nota: término.inicio.básico describe el orden en el que se destruyen los objetos locales con duración de almacenamiento estático.]

La memoria para todas las variables estáticas se asigna durante la carga del programa.Pero las variables estáticas locales se crean e inicializan la primera vez que se usan, no al iniciar el programa.Hay buenas lecturas sobre eso y sobre estática en general. aquí.En general, creo que algunos de estos problemas dependen de la implementación, especialmente si desea saber en qué parte de la memoria se ubicarán estas cosas.

El compilador asignará variables estáticas definidas en una función. foo al cargar el programa, sin embargo, el compilador también agregará algunas instrucciones adicionales (código de máquina) a su función foo de modo que la primera vez que se invoque, este código adicional inicializará la variable estática (p. ej.invocando al constructor, si corresponde).

@Adán:Esta inyección de código detrás de escena por parte del compilador es la razón del resultado que vio.

Intento probar nuevamente el código de Adam Pierce y agregó dos casos más:variable estática en clase y tipo POD.Mi compilador es g++ 4.8.1, en el sistema operativo Windows (MinGW-32).El resultado es que la variable estática en la clase se trata de la misma manera que la variable global.Se llamará a su constructor antes de ingresar a la función principal.

  • Conclusión (para g++, entorno Windows):

    1. Variable global y miembro estático en clase:Se llama al constructor antes de entrar. principal función (1).
    2. variable estática local:El constructor solo se llama cuando la ejecución alcanza su declaración por primera vez.
    3. Si La variable estática local es de tipo POD, entonces también se inicializa antes de ingresar principal función (1).Ejemplo de tipo POD: número int estático = 10;

(1):El estado correcto debería ser: "antes de llamar a cualquier función de la misma unidad de traducción". Sin embargo, en casos simples, como en el ejemplo siguiente, entonces es principal función.

incluir <iostream>

#include < string>

using namespace std;

class test
{
public:
   test(const char *name)
            : _name(name)
    {
            cout << _name << " created" << endl;
    }

    ~test()
    {
            cout << _name << " destroyed" << endl;
    }

    string _name;
    static test t; // static member
 };
test test::t("static in class");

test t("global variable");

void f()
{
    static  test t("static variable");
    static int num = 10 ; // POD type, init before enter main function

    test t2("Local variable");
    cout << "Function executed" << endl;
}

int main()
{
    test t("local to main");
    cout << "Program start" << endl;
    f();
    cout << "Program end" << endl;
    return 0;
 }

resultado:

static in class created
global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed
static in class destroyed

¿Alguien probó en Linux env?

Las variables estáticas se asignan dentro de un segmento de código; son parte de la imagen ejecutable y, por lo tanto, están asignadas ya inicializadas.

Las variables estáticas dentro del alcance de la función se tratan de la misma manera; el alcance es puramente una construcción a nivel de lenguaje.

Por este motivo, se le garantiza que una variable estática se inicializará en 0 (a menos que especifique algo más) en lugar de un valor indefinido.

Hay otras facetas de la inicialización que puede aprovechar; por ejemplo, los segmentos compartidos permiten que diferentes instancias de su ejecutable se ejecuten a la vez para acceder a las mismas variables estáticas.

En C++ (de ámbito global), los objetos estáticos tienen sus constructores llamados como parte del inicio del programa, bajo el control de la biblioteca de tiempo de ejecución de C.En Visual C++, al menos el orden en el que se inicializan los objetos puede controlarse mediante el inicio_seg pragma.

¿O se inicializa cuando se llama por primera vez a doSomething()?

Sí, lo es.Esto, entre otras cosas, le permite inicializar estructuras de datos a las que se accede globalmente cuando sea apropiado, por ejemplo, dentro de bloques try/catch.P.ej.en lugar de

int foo = init(); // bad if init() throws something

int main() {
  try {
    ...
  }
  catch(...){
    ...
  }
}

puedes escribir

int& foo() {
  static int myfoo = init();
  return myfoo;
}

y úselo dentro del bloque try/catch.En la primera llamada, la variable se inicializará.Luego, en la primera y siguiente llamada, se devolverá su valor (por referencia).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top