Quando le variabili statiche a livello di funzione vengono allocate/inizializzate?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Sono abbastanza fiducioso che le variabili dichiarate a livello globale vengano allocate (e inizializzate, se applicabile) all'avvio del programma.

int globalgarbage;
unsigned int anumber = 42;

Ma che dire di quelli statici definiti all'interno di una funzione?

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

Quando c'è spazio per globalish assegnato?Immagino quando inizierà il programma.Ma viene inizializzato anche allora?Oppure è inizializzato quando doSomething() viene chiamato per primo?

È stato utile?

Soluzione

Ero incuriosito, quindi ho scritto il seguente programma di test e l'ho compilato con g++ versione 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;
}

I risultati non erano quelli che mi aspettavo.Il costruttore dell'oggetto statico non è stato chiamato fino alla prima chiamata della funzione.Ecco l'output:

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

Altri suggerimenti

Alcuni termini rilevanti dallo standard C++:

3.6.2 Inizializzazione di oggetti non locali [basic.start.init]

1

L'archiviazione per oggetti con durata di archiviazione statica (basic.stc.static) deve essere inizializzato con zero (dcl.init) prima che si verifichi qualsiasi altra inizializzazione.Oggetti di tipi di pod (tipi.base) con durata di archiviazione statica inizializzata con espressioni costanti (cost.espr) deve essere inizializzato prima che avvenga qualsiasi inizializzazione dinamica.Gli oggetti dell'ambito dello spazio dei nomi con durata di archiviazione statica definita nella stessa unità di traduzione e inizializzati dinamicamente devono essere inizializzati nell'ordine in cui la loro definizione appare nell'unità di traduzione.[Nota: dcl.init.aggr Descrive l'ordine in cui vengono inizializzati i membri aggregati.L'inizializzazione degli oggetti statici locali è descritta in stmt.dcl. ]

[altro testo sotto aggiunge più libertà agli autori di compilatori]

6.7 Dichiarazione di dichiarazione [stmt.dcl]

...

4

L'inizializzazione zero (dcl.init) di tutti gli oggetti locali con durata di archiviazione statica (basic.stc.static) viene eseguito prima che si verifichi qualsiasi altra inizializzazione.Un oggetto locale di tipo POD (tipi.base) con la durata di archiviazione statica inizializzata con espressioni costanti viene inizializzata prima che il suo blocco venga inserito per la prima volta.Un'implementazione è consentita di eseguire l'inizializzazione precoce di altri oggetti locali con durata di archiviazione statica nelle stesse condizioni in cui un'implementazione è consentita di inizializzare staticamente un oggetto con durata di archiviazione statica nell'ambito dello spazio dei nomi (basic.start.init).Altrimenti tale oggetto viene inizializzato, il controllo per la prima volta passa attraverso la sua dichiarazione;Tale oggetto è considerato inizializzato al completamento della sua inizializzazione.Se l'inizializzazione esce lanciando un'eccezione, l'inizializzazione non è completa, quindi verrà nuovamente riprovato che il successivo controllo del tempo entrerà nella dichiarazione.Se il controllo rientra nella dichiarazione (ricorsivamente) mentre l'oggetto viene inizializzato, il comportamento non è definito.[Esempio:

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

--esempio finale]

5

Il distruttore per un oggetto locale con durata di archiviazione statica verrà eseguito se e solo se la variabile è stata costruita.[Nota: termine.iniziale.di.base Descrive l'ordine in cui vengono distrutti gli oggetti locali con durata di archiviazione statica.]

La memoria per tutte le variabili statiche viene allocata al caricamento del programma.Ma le variabili statiche locali vengono create e inizializzate la prima volta che vengono utilizzate, non all'avvio del programma.C'è qualche buona lettura a riguardo, e sulla statica in generale, Qui.In generale penso che alcuni di questi problemi dipendano dall'implementazione, soprattutto se vuoi sapere dove verranno posizionati questi elementi in memoria.

Il compilatore assegnerà le variabili statiche definite in una funzione foo al caricamento del programma, tuttavia, il compilatore aggiungerà anche alcune istruzioni aggiuntive (codice macchina) alla tua funzione foo in modo che la prima volta che viene invocato questo codice aggiuntivo inizializzerà la variabile statica (es.invocando il costruttore, se applicabile).

@Adamo:Questa iniezione di codice dietro le quinte da parte del compilatore è la ragione del risultato che hai visto.

Provo a testare nuovamente il codice da Adamo Pierce e ha aggiunto altri due casi:variabile statica in classe e tipo POD.Il mio compilatore è g++ 4.8.1, nel sistema operativo Windows (MinGW-32).Il risultato è che la variabile statica nella classe viene trattata allo stesso modo della variabile globale.Il suo costruttore verrà chiamato prima di inserire la funzione principale.

  • Conclusione (per g++, ambiente Windows):

    1. Variabile globale E membro statico della classe:il costruttore viene chiamato prima di entrare principale funzione (1).
    2. Variabile statica locale:il costruttore viene chiamato solo quando l'esecuzione raggiunge la sua dichiarazione per la prima volta.
    3. Se La variabile statica locale è di tipo POD, quindi viene inizializzato anche prima di entrare principale funzione (1).Esempio per il tipo POD: numero intero statico = 10;

(1):Lo stato corretto dovrebbe essere: "prima che venga chiamata qualsiasi funzione della stessa unità di traduzione". Tuttavia, per semplicità, come nell'esempio seguente, lo è principale funzione.

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

risultato:

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

Qualcuno ha testato in Linux env?

Le variabili statiche vengono allocate all'interno di un segmento di codice: fanno parte dell'immagine eseguibile e quindi sono mappate già inizializzate.

Le variabili statiche all'interno dell'ambito della funzione vengono trattate allo stesso modo, l'ambito è puramente un costrutto a livello di linguaggio.

Per questo motivo hai la garanzia che una variabile statica verrà inizializzata su 0 (a meno che non specifichi qualcos'altro) anziché su un valore indefinito.

Esistono altri aspetti dell'inizializzazione di cui puoi trarre vantaggio: ad esempio i segmenti condivisi consentono a diverse istanze del tuo eseguibile in esecuzione contemporaneamente di accedere alle stesse variabili statiche.

In C++ (con ambito globale) gli oggetti statici hanno i loro costruttori chiamati come parte dell'avvio del programma, sotto il controllo della libreria runtime C.In Visual C++ almeno l'ordine in cui gli oggetti vengono inizializzati può essere controllato da init_seg pragma.

Oppure viene inizializzato quando viene chiamato per la prima volta doSomething()?

Sì.Questo, tra le altre cose, consente di inizializzare le strutture dati ad accesso globale quando appropriato, ad esempio all'interno dei blocchi try/catch.Per esempio.invece di

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

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

tu puoi scrivere

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

e usarlo all'interno del blocco try/catch.Alla prima chiamata, la variabile verrà inizializzata.Quindi, alla prima chiamata e a quelle successive, verrà restituito il suo valore (per riferimento).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top