Kiedy zmienne statyczne na poziomie funkcji są przydzielane/inicjalizowane?

Jestem całkiem pewien, że globalnie zadeklarowane zmienne są przydzielane (i inicjalizowane, jeśli ma to zastosowanie) w momencie rozpoczęcia programu.

int globalgarbage;
unsigned int anumber = 42;

Ale co ze statycznymi zdefiniowanymi w funkcji?

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

Kiedy jest przydzielona przestrzeń dla globalish? Zgaduję, kiedy zacznie się program. Ale czy wtedy też zostanie zainicjowany? Czy jest inicjalizowana, gdy doSomething() jest wywoływana po raz pierwszy?

 76
Author: Owen, 2008-09-11

7 answers

Byłem ciekawy tego, więc napisałem następujący program testowy i skompilowałem go z g++ w wersji 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;
}

Wyniki nie były takie, jak oczekiwałem. Konstruktor obiektu statycznego został wywołany dopiero po pierwszym wywołaniu funkcji. Oto Wyjście:

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
 75
Author: Adam Pierce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 00:18:08

Kilka istotnych wersji ze standardu C++:

3.6.2 Inicjalizacja obiektów nielokalnych [basic.zaczynaj.init]

1

Przechowywanie obiektów ze statycznym przechowywaniem czas trwania ( podstawowy.stc.statyczny ) jest zerowo inicjowany (dcl.init ) przed jakąkolwiek inną inicjalizacją. Obiekty Typy POD (basic.typy ) ze statycznym czasem przechowywania zainicjalizowana stałą expressions ( expr.const ) jest inicjalizacja przed jakąkolwiek dynamiczną inicjalizacją. Obiekty zakresu przestrzeni nazw ze statycznym czasem przechowywania zdefiniowanym w ta sama jednostka tłumaczenia i dynamicznie inicjowana jest inicjowane w kolejności, w jakiej ich definicja pojawia się w Biuro Tłumaczeń. [Uwaga: dcl.init.aggr opisuje kolejność, w jakiej inicjowane są członkowie zbiorczy. Na inicjalizacja lokalnych obiektów statycznych jest opisany w stmt.dcl . ]

[więcej tekstu poniżej dodając więcej swobody dla autorów kompilatorów]

6.7 deklaracja [stmt.dcl]

...

4

The zero-initialization (dcl.init ) wszystkich lokalnych obiektów z statyczny czas przechowywania (basic.stc.static ) jest wykonywana przed każda inna inicjalizacja ma miejsce. Lokalny obiekt Typ POD ( podstawowe.typy ) ze statycznym czasem przechowywania initialized with constant-expressions is initialized before its blok jest wprowadzany jako pierwszy. Realizacja jest dozwolona do wykonania wczesna inicjalizacja innych obiektów lokalnych ze statycznym przechowywaniem czas trwania na tych samych warunkach, co realizacja dozwolone statyczne inicjowanie obiektu za pomocą statycznej pamięci masowej czas trwania w przestrzeni nazw scope (basic.zaczynaj.init ). W przeciwnym razie takie obiekt jest inicjowany pierwsza kontrola czasu przechodzi przez jego deklaracji; taki obiekt jest uważany za zainicjowany po zakończenie jego inicjalizacji. Jeśli inicjalizacja kończy się przez rzucając wyjątek, inicjalizacja nie jest kompletna, więc będzie spróbuj ponownie przy następnym wprowadzeniu deklaracji przez control. Jeśli control ponownie wprowadzi deklarację (rekurencyjnie) podczas gdy obiekt jest zainicjalizowane, zachowanie jest niezdefiniowane. [ przykład:

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

--end przykład ]

5

Destruktor dla obiektu lokalnego o statycznym czasie przechowywania będzie być wykonywane wtedy i tylko wtedy, gdy zmienna została skonstruowana. [Uwaga: podstawowe.zaczynaj.termin opisuje kolejność, w jakiej lokalne obiekty ze statycznym czasem przechowywania są niszczone. ]

 46
Author: Arkadiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-31 14:35:24

Pamięć dla wszystkich zmiennych statycznych jest przydzielana przy załadowaniu programu. Ale lokalne zmienne statyczne są tworzone i inicjowane przy pierwszym użyciu, a nie przy uruchomieniu programu. Jest na ten temat dobra lektura i ogólnie statyka tutaj. Ogólnie rzecz biorąc, myślę, że niektóre z tych problemów zależą od implementacji, zwłaszcza jeśli chcesz wiedzieć, gdzie w pamięci te rzeczy będą się znajdować.

 22
Author: Eugene,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 18:55:50

Kompilator przydziela zmienną (- y) statyczną (- e) zdefiniowaną (- e) w funkcji foo podczas ładowania programu, jednakże kompilator doda również dodatkowe instrukcje (kod maszynowy) do funkcji foo tak, że przy pierwszym wywołaniu ten dodatkowy kod zainicjalizuje zmienną statyczną (np. wywołanie konstruktora, jeśli ma to zastosowanie).

@Adam: to za kulisami wtrysku kodu przez kompilator jest powodem, dla którego wynik widziałeś.

 9
Author: Henk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 01:09:04

Próbuję ponownie przetestować kod z Adam Pierce i dodałem jeszcze dwa przypadki: zmienna statyczna w klasie i typ POD. Mój kompilator to g++ 4.8.1, w systemie operacyjnym Windows (MinGW-32). Wynikiem jest zmienna statyczna w klasie jest traktowana tak samo ze zmienną globalną. Jego konstruktor zostanie wywołany przed wejściem funkcji głównej.

  • Podsumowanie (dla środowiska G++, Windows):

    1. zmienna globalna i statyczny członek w klasie : konstruktor jest wywoływany przed wejściem main function (1).
    2. lokalna zmienna statyczna : konstruktor jest wywoływany tylko wtedy, gdy wykonanie osiągnie swoją deklarację za pierwszym razem.
    3. Jeżeli lokalna zmienna statyczna jest typu POD , to jest również inicjalizowana przed wejściem main function (1). Przykład dla typu POD: static int number = 10;

(1): prawidłowym stanem powinno być: "przed jakąkolwiek funkcją z ta sama jednostka tłumaczeniowa nazywa się"". jednak dla prostych, jak w przykładzie poniżej, to jest main funkcja.

Include

#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;
 }

Wynik:

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

Ktoś testował w Linuksie env ?

 4
Author: Thang Le,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-04 13:41:22

Zmienne statyczne są przydzielane wewnątrz segmentu kodu -- są częścią obrazu wykonywalnego, a więc są mapowane w już zainicjowanym.

Zmienne statyczne w zakresie funkcji są traktowane tak samo, zakres jest konstrukcją czysto językową.

Z tego powodu masz gwarancję, że zmienna statyczna zostanie zainicjowana na 0 (chyba że podasz coś innego), a nie niezdefiniowaną wartość.

Istnieją inne aspekty inicjalizacji, które możesz wziąć advantage off - na przykład współdzielone segmenty umożliwiają różnym instancjom uruchomionego pliku wykonywalnego dostęp do tych samych zmiennych statycznych.

W C++ (globalnie) obiekty statyczne mają swoje konstruktory wywoływane jako część uruchamiania programu, pod kontrolą biblioteki uruchomieniowej C. W Visual C++ przynajmniej kolejność inicjalizacji obiektów może być kontrolowana przez pragma init_seg .

 3
Author: Rob Walker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 00:44:47

Czy jest inicjalizowana, gdy doSomething() jest wywoływane po raz pierwszy?

Tak, jest. Pozwala to między innymi inicjalizować globalnie dostępne struktury danych, gdy jest to właściwe, na przykład wewnątrz bloków try/catch. Np. zamiast
int foo = init(); // bad if init() throws something

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

Możesz napisać

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

I użyj go wewnątrz bloku try/catch. Przy pierwszym wywołaniu zmienna zostanie zainicjalizowana. Następnie przy pierwszym i następnym wywołaniu zwracana jest jego wartość (przez odniesienie).

 3
Author: dmityugov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-31 14:07:39