2014-03-01 16 views
33

Ich verstehe nicht, was das folgende Codebeispiel tut und wie sie es tut:C++ globale Variable Initialisierungsreihenfolge

#include <stdio.h> 

int f(); 

int a = f(); // a exists just to call f 

int x = 22; 

int f() { 
    ++x; 
    return 123; // unimportant arbitrary number 
} 

int main() { 
    printf("%d\n", x); 
} 

Wenn dies es 23 lief druckt, was die intuitive Antwort.

In C++ sind jedoch globale Variablen supposed to be in der Reihenfolge der Definition initialisiert. Das würde bedeuten, dass a vor x initialisiert werden sollte, weil es vor x definiert ist. Wenn dies der Fall wäre, müsste die Funktion f aufgerufen werden, bevor x initialisiert wurde, da der Aufruf an f ein Teil der Definition a ist.

Wenn f ist in der Tat aufgerufen, bevor x initialisiert wird, würde das bedeuten, dass f würde versuchen x zu erhöhen - das Ergebnis von denen ich bin nicht wirklich sicher (höchstwahrscheinlich UB, oder einem Kauderwelsch-Wert). Nachdem a initialisiert wurde, wird x auf 22 initialisiert und das Programm würde 22 ausdrucken.

Offensichtlich passiert das nicht, was passiert. Aber was ist? Was macht der Code eigentlich?

Es scheint definitiv wie x-22 vor a = f() gesetzt wird ausgewertet, aber das würde bedeuten, dass die Reihenfolge der Initialisierung umgekehrt wird (ich könnte auch falsch sein, was die Initialisierung ist, oder wenn es passiert).

Antwort

33

Das Problem ist ein bisschen subtil; Details finden Sie in C++ 11 3.6.2.

Was für uns zählt, ist, dass es zwei Phasen der Initialisierung von „nicht-lokalen Variablen mit statischer Speicherdauer“ (oder „globaler Variablen“ in dem Umgangssprachgebrauch): die statische Initialisierungsphase und die dynamische Initialisierungsphase. Die statische Phase kommt zuerst. Es sieht wie folgt aus:

int a = 0; 
int x = 22; 

Die dynamische Initialisierung läuft danach:

a = f(); 

Der Punkt ist, dass statische Initialisierung nicht „run“ überhaupt - es besteht nur die Werte der Einstellung, die bei bekannten Kompilierzeit, so dass diese Werte bereits vor der Ausführung festgelegt werden. Was die Initialisierung int x = 22; static macht, ist, dass der Initialisierer ein konstanter Ausdruck ist.


Es gibt Fälle, in denen dynamische Initialisierung kann auf die statische Phase gehisst (aber muss nicht), aber dies ist nicht einer jener Fälle, weil es nicht die Anforderung nicht erfüllt, dass

die dynamische Version der Initialisierung den Wert jedes andere Objekt nicht Umfang Namespace vor seiner Initialisierung

ändern Wenn diese Hebe geschieht, ist es zulässig, dass die resultierenden Anfangswerte von denen abweichen können, wenn sie nicht vorkommen. Es gibt ein Beispiel im Standard für eine solche "unbestimmte" Initialisierung. Auch

+0

So im Wesentlichen wird die Initialisierung in den nicht-Seite bewirkende Teil getrennt, dass „runs“ erster (keine Funktionen, Code nur Baugruppe, die den Speicher verändert und inkrementiert den Stapelzeiger) und einen Nebeneffektteil, der als zweiter läuft (in dem die tatsächlichen Funktionen ausgeführt werden). Und wenn eine Funktion nachweislich keine Nebenwirkungen hat, kann sie in den ersten Teil gehievt werden. Verstehe ich es richtig? Es macht Sinn. Außerdem hast du 'x = f();' geschrieben, aber ich denke, du meintest 'a = f(); '(das steht in meinem Code). – corazza

+0

Auch der erste Satz Ihres letzten Absatzes ist ein wenig seltsam: * Wenn dieses Hochziehen passiert, ist es zulässig, dass die resultierenden Anfangswerte anders sein können als wenn es nicht passiert wäre. * - was meinst du? Es sieht irgendwie so aus, als hättest du ein "es" verpasst, aber im Allgemeinen ist das, was du beschreibst, ein bisschen verwirrend, vielleicht würde das Posten dieses Beispiels helfen? – corazza

+1

@yannbane: Das Beispiel ist in dem Abschnitt, den ich zitiert habe. Ich zögere, große Teile davon zu kopieren, weil Sie im Wesentlichen alles lesen sollten. Ich empfehle dir [gehe zu github] (https://github.com/cplusplus/draft) und schnappe dir eine Kopie des Standards. Ja, es gibt zwei Phasen. Die erste Phase verändert nicht einmal den Speicher - die Anfangswerte werden einfach in die Binärdatei geschrieben und vom Loader in den Speicher geladen. –

4

betrachten:

#include <iostream> 
using namespace std; 

int f(); 
int g(); 

int a = f(); 
int b = g(); 

int main() { 
     cout << a << " " << b; 
} 

int f() { 
     b++; 
     cout << "f" << endl; 
     return 1; 
} 

int g() { 
     cout << "g" << endl; 
     return 2; 
} 

Der Ausgang ist:

f 
g 
1 2 

Ersetzen b = g(); mit b = 22; verursacht 1 23 gedruckt werden. Die Antwort von Kerrek SB erklärt, warum dies so ist.

-5

Sie können jederzeit Ihr Programm wie dieses umschreiben:

#include <stdio.h> 

int f(int x) { 
    ++x; 
    return x; 
} 

int main() { 
    printf("%d\n", f(22)); 
} 
+0

Es tut uns leid, aber Ihr Beispiel kompiliert nicht als C++ - Code wegen der 'a = f()'. – blackbird

+0

@blackbird Dann werde ich das ändern. –

+0

Nun, das ist ein schöner Funktionsaufruf, aber das Thema hier ist die Initialisierung der globalen Variablen, und Sie haben sie alle los :) – blackbird