c++ - palabra - variable static java
¿Cuándo se asignan/inicializan las variables estáticas a nivel de función? (7)
¿O se inicializa cuando se llama primero a DoSomething ()?
Sí lo es. Esto, entre otras cosas, le permite inicializar estructuras de datos a las que se accede a nivel mundial cuando es apropiado, por ejemplo, dentro de bloques try / catch. Por ejemplo, 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 usarlo dentro del bloque try / catch. En la primera llamada, la variable se inicializará. Luego, en la primera y las siguientes llamadas, su valor será devuelto (por referencia).
Estoy bastante seguro de que las variables declaradas a nivel mundial se asignan (y se inicializan, si corresponde) a la hora de inicio del programa.
int globalgarbage;
unsigned int anumber = 42;
Pero, ¿qué hay de los estáticos definidos dentro de una función?
void doSomething()
{
static bool globalish = true;
// ...
}
¿Cuándo se asigna el espacio para globalish
? Supongo que cuando el programa comience. Pero, ¿se inicializa entonces también? ¿O se inicializa cuando se llama primero a doSomething()
?
Algunas verborreas relevantes de C ++ Standard:
3.6.2 Inicialización de objetos no locales [basic.start.init]
1
El almacenamiento para objetos con duración de almacenamiento estático ( basic.stc.static ) se inicializará en cero ( dcl.init ) antes de que se lleve a cabo cualquier otra inicialización. Los objetos de tipos POD ( basic.types ) con duración de almacenamiento estático inicializados con expresiones constantes ( expr.const ) se inicializarán antes de que tenga lugar cualquier inicialización dinámica. Los objetos del ámbito de espacio de nombre con duración de almacenamiento estático definidos en la misma unidad de traducción e inicializados dinámicamente se inicializarán en el orden en que aparece su definición en la unidad de traducción. [Nota: dcl.init.aggr describe el orden en que se inicializan los miembros agregados. La inicialización de objetos estáticos locales se describe en stmt.dcl . ]
[más texto debajo para agregar más libertades para los escritores de compiladores]
6.7 Declaración de declaración [stmt.dcl]
...
4
La inicialización a cero ( dcl.init ) de todos los objetos locales con duración de almacenamiento estático ( basic.stc.static ) se realiza antes de que tenga lugar cualquier otra inicialización. Un objeto local de tipo POD ( basic.types ) con duración de almacenamiento estático inicializado con expresiones constantes se inicializa antes de que se ingrese por primera vez. Se permite que una implementación realice la inicialización temprana de otros objetos locales con duración de almacenamiento estático en las mismas condiciones en que se permite que una implementación inicialice de forma estática un objeto con duración de almacenamiento estático en el ámbito del espacio de nombres ( basic.start.init ). De lo contrario, dicho objeto se inicializa la primera vez que el control pasa por su declaración; dicho objeto se considera inicializado una vez completada su inicialización. Si la inicialización finaliza arrojando una excepción, la inicialización no está completa, por lo que se volverá a intentar la próxima vez que el control ingrese la declaración. Si el control vuelve a entrar en la declaración (recursivamente) mientras el objeto se está inicializando, el comportamiento no está definido. [ 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á solo si la variable se construyó. [Nota: basic.start.term describe el orden en que se destruyen los objetos locales con duración de almacenamiento estático. ]
El compilador asignará las 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
para que la primera vez que se invoca este código adicional inicialice la estática variable (por ejemplo, invocar el constructor, si corresponde).
@Adam: Esta inyección de código entre bastidores por parte del compilador es la razón del resultado que vio.
Intento probar nuevamente el código de Adam Pierce y agregué dos casos más: variable estática en clase y tipo de POD. Mi compilador es g ++ 4.8.1, en el sistema operativo Windows (MinGW-32). El resultado es una variable estática en la clase que se trata igual con la variable global. Se llamará a su constructor antes de ingresar a la función principal.
Conclusión (para g ++, entorno de Windows):
- Variable global y miembro estático en clase : se llama al constructor antes de ingresar a la función principal (1) .
- Variable estática local : el constructor solo se invoca cuando la ejecución alcanza su declaración por primera vez.
- Si la variable estática local es del tipo POD , entonces también se inicializa antes de ingresar a la función principal (1) . Ejemplo para el tipo de 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, por simple, como en el ejemplo a continuación, entonces es la función principal .
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 probado en Linux env?
La memoria para todas las variables estáticas se asigna a la carga del programa. Pero las variables estáticas locales se crean y se inicializan la primera vez que se usan, no al inicio del programa. Hay una buena lectura sobre eso, y la estática en general, here . En general, creo que algunos de estos problemas dependen de la implementación, especialmente si quiere saber en qué lugar de memoria se ubicará esta información.
Las variables estáticas se asignan dentro de un segmento de código: son parte de la imagen ejecutable y, por lo tanto, se asignan en un archivo inicializado.
Las variables estáticas dentro del alcance de la función se tratan de la misma manera, el alcance es una construcción puramente a nivel de lenguaje.
Por esta razón, tiene la garantía de que una variable estática se inicializará a 0 (a menos que especifique algo más) en lugar de un valor indefinido.
Hay otras facetas de la inicialización con las 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 ++ (alcance global), los objetos estáticos tienen sus constructores llamados como parte del inicio del programa, bajo el control de la biblioteca C runtime. En Visual C ++, al menos el orden en que se inicializan los objetos puede controlarse mediante init_seg pragma.
Tenía curiosidad acerca de esto, así que escribí el siguiente programa de prueba y lo compilé con la versión 4.1.2 de g ++.
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 para el objeto estático no se invocó 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