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


91

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

int globalgarbage;
unsigned int anumber = 42;

Ma per quanto riguarda quelli statici definiti all'interno di una funzione?

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

Quando viene globalishassegnato lo spazio ? Immagino quando inizierà il programma. Ma viene inizializzato anche allora? O viene inizializzato quando doSomething()viene chiamato per la prima volta?

Risposte:


93

Ero curioso di questo, 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 sono stati quelli che mi aspettavo. Il costruttore per l'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

31
Chiarimento: la variabile statica viene inizializzata la prima volta che l'esecuzione colpisce la sua dichiarazione, non quando viene chiamata la funzione contenitore. Se hai solo una statica all'inizio della funzione (ad es. Nel tuo esempio) queste sono le stesse, ma non lo sono necessariamente: ad es. Se hai 'if (...) {static MyClass x; ...} ', quindi' x 'non verrà inizializzata affatto durante la prima esecuzione di quella funzione nel caso in cui la condizione dell'istruzione if sia falsa.
EvanED

4
Ma questo non porta ad un sovraccarico di runtime, poiché ogni volta che viene utilizzata la variabile statica, il programma deve verificare se è stata utilizzata in precedenza, altrimenti deve essere inizializzato? In quel caso quel genere di schifo fa un po 'schifo.
Ciao Arrivederci

illustrazione perfetta
Des1gnWizard

@veio: Sì, l'inizializzazione è thread-safe. Vedi la domanda per maggiori dettagli: stackoverflow.com/questions/23829389/…
Rémi

2
@ HelloGoodbye: sì, porta a un sovraccarico di runtime. Vedi anche quella domanda: stackoverflow.com/questions/23829389/…
Rémi

54

Alcuni termini pertinenti dallo standard C ++:

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

1

La memorizzazione per gli oggetti con durata di memorizzazione statica ( basic.stc.static ) deve essere inizializzata a zero ( dcl.init ) prima che venga eseguita qualsiasi altra inizializzazione. Gli oggetti di tipo POD ( basic.types ) con durata di memorizzazione statica inizializzata con espressioni costanti ( expr.const ) devono essere inizializzati prima che avvenga qualsiasi inizializzazione dinamica. Gli oggetti con ambito dello spazio dei nomi con durata di memorizzazione 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 di seguito che aggiunge ulteriori libertà per gli autori di compilatori]

6.7 Dichiarazione di dichiarazione [stmt.dcl]

...

4

L'inizializzazione zero ( dcl.init ) di tutti gli oggetti locali con durata della memorizzazione statica ( basic.stc.static ) viene eseguita prima di qualsiasi altra inizializzazione. Un oggetto locale di tipo POD ( basic.types ) con durata di memorizzazione statica inizializzata con espressioni costanti viene inizializzato prima che il suo blocco venga inserito per la prima volta. Un'implementazione può eseguire l'inizializzazione anticipata di altri oggetti locali con durata di archiviazione statica alle stesse condizioni in cui è consentita un'implementazione per inizializzare staticamente un oggetto con durata di archiviazione statica nell'ambito dello spazio dei nomi ( basic.start.init). Altrimenti un tale oggetto viene inizializzato la prima volta che il controllo passa attraverso la sua dichiarazione; tale oggetto è considerato inizializzato al termine della sua inizializzazione. Se l'inizializzazione termina generando un'eccezione, l'inizializzazione non è completa, quindi verrà ritentata la prossima volta che il controllo entrerà nella dichiarazione. Se il controllo rientra nella dichiarazione (ricorsivamente) mentre l'oggetto viene inizializzato, il comportamento è indefinito. [ 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: basic.start.term descrive l'ordine in cui vengono eliminati gli oggetti locali con durata di archiviazione statica. ]


Questo ha risposto alla mia domanda e non si basa su "prove aneddotiche" a differenza della risposta accettata. Stavo cercando specificamente questa menzione delle eccezioni nel costruttore di oggetti statici locali di funzioni inizializzate staticamente:If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration.
Bensge

26

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'è una buona lettura su questo, e sulla statica in generale, qui . In generale, penso che alcuni di questi problemi dipendono dall'implementazione, specialmente se vuoi sapere dove si troverà questa roba nella memoria.


2
non proprio, le statistiche locali vengono allocate e inizializzate da zero "al caricamento del programma" (tra virgolette, perché neanche questo è corretto), e quindi reinizializzate la prima volta che viene inserita la funzione in cui si trovano.
Mooing Duck

Sembra che quel collegamento sia ora interrotto, 7 anni dopo.
Steve

1
Sì, collegamento interrotto. Ecco un archivio: web.archive.org/web/20100328062506/http://www.acm.org/…
Eugene

10

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

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


5

Provo a testare di nuovo il codice di Adam Pierce e ho 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 è una variabile statica nella classe che viene trattata allo stesso modo della variabile globale. Il suo costruttore verrà chiamato prima di entrare nella funzione principale.

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

    1. Variabile globale e membro statico in classe : il costruttore viene chiamato prima di entrare nella funzione principale (1) .
    2. Variabile statica locale : il costruttore viene chiamato solo quando l'esecuzione raggiunge la sua dichiarazione la prima volta.
    3. Se la variabile statica locale è di tipo POD , viene inizializzata anche prima di accedere alla funzione principale (1) . Esempio di tipo POD: static int number = 10;

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

includi <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 ambiente Linux?


4

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

Sì. Questo, tra le altre cose, consente di inizializzare strutture di dati a cui si accede globalmente quando è appropriato, ad esempio all'interno dei blocchi try / catch. Ad 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 usalo all'interno del blocco try / catch. Alla prima chiamata, la variabile verrà inizializzata. Quindi, alla prima e alla successiva chiamata, verrà restituito il suo valore (per riferimento).


3

Le variabili statiche sono 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 a 0 (a meno che tu non specifichi qualcos'altro) piuttosto che un valore indefinito.

Ci sono altri aspetti dell'inizializzazione che 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 di runtime C. In Visual C ++ almeno l'ordine in cui vengono inizializzati gli oggetti può essere controllato dal pragma init_seg .


4
Questa domanda riguarda la statica con ambito di funzione. Almeno quando hanno costruttori non banali vengono inizializzati al primo ingresso nella funzione. O più specificamente, quando viene raggiunta quella linea.
Adam Mitz,

Vero, ma la domanda parla dello spazio assegnato alla variabile e utilizza tipi di dati semplici. Lo spazio è ancora allocato nel segmento di codice
Rob Walker,

Non vedo come il segmento di codice e il segmento di dati siano davvero importanti qui. Penso che abbiamo bisogno di chiarimenti dal PO. Ha detto "e inizializzato, se applicabile".
Adam Mitz

5
le variabili non vengono mai allocate all'interno del segmento di codice; in questo modo non sarebbero in grado di scrivere.
botismarius

1
alle variabili statiche viene allocato spazio nel segmento dati o nel segmento bss a seconda che siano inizializzate o meno.
EmptyData
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.