Jak je inicializace statická proměnná realizován kompilátor?

hlasů
19

Jsem zvědavý na podkladové implementaci statických proměnných v rámci funkce.

Mám-li deklarovat statické proměnné základního typu (char, int, double, atd.), A dát mu počáteční hodnotu, jsem si představit, že překladač jednoduše nastaví hodnotu této proměnné na samém začátku programu, než main()se nazývá :

void SomeFunction();

int main(int argCount, char ** argList)
{
    // at this point, the memory reserved for 'answer'
    // already contains the value of 42
    SomeFunction();
}

void SomeFunction()
{
    static int answer = 42;
}

Nicméně, v případě, že statická proměnná je instance třídy:

class MyClass
{
    //...
};

void SomeFunction();

int main(int argCount, char ** argList)
{
    SomeFunction();
}

void SomeFunction()
{
    static MyClass myVar;
}

Vím, že to nebude inicializována dokud se poprvé, že funkce se nazývá. Vzhledem k tomu, že kompilátor nemá žádný způsob, jak zjistit, kdy bude funkce volána poprvé, jak to vyrobit tento problém vyřešit? Znamená to v podstatě zavést if-blok do těla funkce?

static bool initialized = 0;
if (!initialized)
{
    // construct myVar
    initialized = 1;
}
Položena 22/05/2009 v 16:20
zdroj uživatelem
V jiných jazycích...                            


5 odpovědí

hlasů
11

Ve výstupu kompilátoru jsem viděl, funkční místní statické proměnné jsou inicializovány přesně tak, jak si představujete.

Všimněte si, že obecně to není provedeno v thread-safe způsobem. Takže pokud máte funkce se statickými místními obyvateli, jako je to, že by se dalo nazvat z více vláken, byste měli vzít v úvahu. Volání funkce jednou v hlavním vlákně ne¾ ostatní se nazývají obvykle stačit.

Měl bych dodat, že v případě, že inicializace místní statické je prostou konstantou, jako v příkladu, kompilátor nepotřebuje projít těmito výkyvy - to může jen inicializovat proměnné v obraze nebo dříve main()jako normální statické inicializaci ( protože váš program by nebyli schopni poznat rozdíl). Ale pokud si ji inicializovat s návratovou hodnotou funkci, pak kompilátor do značné míry musí otestovat příznak označující, pokud inicializace byla provedena, nebo něco ekvivalent.

Odpovězeno 22/05/2009 v 16:29
zdroj uživatelem

hlasů
2

Máte pravdu o všem, včetně inicializační vlajky jako společné implementace. To je v podstatě důvod, proč inicializace statických místních není thread-safe, a proč pthread_once existuje.

Jeden mírné námitka: překladač musí vyzařovat kód, který „se chová, jako kdyby“ statická lokální proměnná je vytvořena první, kdy je využito. Vzhledem k tomu, inicializace integer nemá žádné vedlejší účinky (a žádá, aby žádný kód uživatele), je to na kompilátor při inicializuje int. Uživatelský kód nemůže „legitimně“ zjistit, co to dělá.

Je zřejmé, můžete se podívat na assembleru, nebo vyvolat nedefinované chování a provádět odpočty z toho, co se vlastně děje. Ale C ++ standardní nepočítá, že jako platné důvody k tvrzení, že chování není „jako kdyby“ to udělal, co spec říká.

Odpovězeno 22/05/2009 v 16:32
zdroj uživatelem

hlasů
1

Vím, že to nebude inicializována dokud se poprvé, že funkce se nazývá. Vzhledem k tomu, že kompilátor nemá žádný způsob, jak zjistit, kdy bude funkce volána poprvé, jak to vyrobit tento problém vyřešit? Znamená to v podstatě zavést if-blok do těla funkce?

Ano, to je pravda: a FWIW, že to není nutně thread-safe (pokud je funkce dvou nití s ​​názvem „poprvé“ současně).

Z tohoto důvodu byste raději definovat proměnné v globálním měřítku (i když třeba ve třídě nebo názvů či statické bez externího propojení) namísto uvnitř funkce, takže to inicializaci před začátkem programu bez run-time „pokud“ ,

Odpovězeno 22/05/2009 v 16:32
zdroj uživatelem

hlasů
10

Tato otázka se vztahuje podobnou půdu, ale bezpečnostní vlákno nebylo zmíněno. Za to, co stojí za to, se C ++ 0x aby funkce statická inicializace vlákno v bezpečí.

(viz C ++ 0x směrnice o zajištění , 6,7 / 4 na funkčních statiky: „Pokud kontrola vstoupí prohlášení současně zatímco proměnná je inicializována, musí souběžné spuštění počkat na dokončení inicializace.“)

Ještě jedna věc, která nebyla zmíněna je, že funkce statika jsou zničeny v opačném pořadí podle jejich konstrukce, takže kompilátor udržuje seznam destruktory se volají při vypnutí (to může nebo nemusí být stejný seznam, který atexit použití).

Odpovězeno 22/05/2009 v 16:51
zdroj uživatelem

hlasů
1

Další kroucení je vložený kód, kde je run-before-main () kód (cinit / cokoliv) může kopírovat předem inicializuje data (oba statiky a non-statika) do paměti RAM z const datového segmentu, snad s bydlištěm v paměti ROM. To je užitečné, pokud kód nemůže být spuštěn z jakési podkladové úložiště (disk), kde může být znovu načten z. Opět platí, že to není v rozporu s požadavky jazyka, protože toto je děláno před main ().

Mírný tangent: I když jsem neviděl to dělá hodně (mimo Emacs), program nebo kompilátor by v podstatě spustit svůj kód do procesu a konkretizovat / inicializovat objekty, pak zmrazit a výpisem proces. Emacs dělá něco podobného, ​​aby to naložit velké množství Elisp (tj žvýkat na to), pak vypsat běžící stav jako pracovní spustitelný soubor, aby se zabránilo náklady na analýze na každém vyvolání.

Odpovězeno 22/05/2009 v 17:27
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more