2010-06-14 11 views
17

ich aus einem Buch über SIOF zu lesen und es gab ein Beispiel:statisches Initialisierungsreihenfolge Fiasko

//file1.cpp 
extern int y; 
int x=y+1; 

//file2.cpp 
extern int x; 
int y=x+1; 

Jetzt Meine Frage ist:
In obigem Code werden passieren folgende Dinge?

  1. während Compiler file1.cpp, Compiler verlässt y, wie es ist, d. H. Speicher für es nicht zuweisen.
  2. Compiler reserviert Speicher für x, aber initialisiert es nicht.
  3. Während Compiler file2.cpp, Compiler verlässt x wie es ist, d. H. Nicht Speicher für es zuzuweisen.
  4. Compiler reserviert Speicher für y, aber initialisiert es nicht.
  5. Während file1.o und file2.o verlinken, lasst nun file2.o zuerst initialisiert werden, also jetzt:
    Erhält x den Anfangswert von 0? oder wird nicht initialisiert?

Antwort

10

Die Initialisierungsschritte in 3.6.2 „Initialisierung nicht lokaler Objekte“ des C++ Standard angegeben:

Schritt 1: x und y Null initialisiert, bevor eine andere Initialisierung stattfindet.

Schritt 2: x oder y wird dynamisch initialisiert - welches ist nicht durch den Standard angegeben.Diese Variable erhält den Wert 1, da die andere Variable auf Null initialisiert wurde.

Schritt 3: Die andere Variable wird dynamisch initialisiert und erhält den Wert 2.

+0

Danke..Aber in Schritt 1, wenn wll x und y Null initialisiert werden: zur Kompilierzeit oder zur Verbindungszeit? –

+6

@Happy Mittal: Sie können nicht sagen, und so kann der Compiler wählen. Es könnte sogar zur Programmladezeit sein. – MSalters

1

Es ist compilerabhängig und kann von der Laufzeit abhängig sein. Ein Compiler kann beschließen, statische Variablen langsam zu initialisieren, wenn auf die erste Variable in einer Datei zugegriffen wird oder auf jede Variable zugegriffen wird. Andernfalls werden alle statischen Variablen nach Datei beim Start initialisiert, wobei die Reihenfolge normalerweise von der Verknüpfungsreihenfolge der Dateien abhängt. Die Dateireihenfolge könnte sich aufgrund von Abhängigkeiten oder anderen kompilerabhängigen Einflüssen ändern.

Statische Variablen werden normalerweise auf Null initialisiert, es sei denn, sie haben einen konstanten Initialisierer. Dies ist wiederum vom Compiler abhängig. Daher wird eine dieser Variablen wahrscheinlich Null sein, wenn die andere initialisiert wird. Da beide Initialisierer haben, können einige Compiler die Werte jedoch undefiniert lassen.

denke ich, das wahrscheinlichste Szenario wäre:

  1. Raum für die Variablen zugeordnet ist, und beide haben den Wert 0.
  2. Eine Variable, sagen x, initialisiert und auf den Wert 1 .
  3. anderen die, sagen wir y, initialisiert und auf den Wert 2.

Sie immer es laufen konnte und sehen gesetzt. Es könnte sein, dass einige Compiler Code generieren, der in eine Endlosschleife übergeht.

+0

Aber mein Zweifel ist, dass, wenn Compiler Speicher für extern int y nicht zuordnen, dann, wie es es auf Null initialisieren kann? Oder wenn y auf Null initialisiert wird? Zur Kompilierzeit oder zur Verbindungszeit? Gibt es Regeln, welche Variable beim Initialisieren erhält? –

+0

Kannst du Schritt für Schritt erklären, was bei Kompilier- und Linkzeit passieren würde/kann? –

2

Der ganze Punkt (und der Grund, warum es ein "Fiasko" genannt wird) ist, dass es unmöglich ist, mit Sicherheit zu sagen was wird in einem Fall wie diesem passieren. Im Wesentlichen fordern Sie etwas Unmögliches (dass zwei Variablen jeweils eine größer als die andere sind). Da sie das nicht können, ist das, was sie tun werden, für einige Fragen offen - sie könnten 0/1, oder 1/0, oder 1/2, oder 2/1, oder möglicherweise (bester Fall) nur einen Fehler erzeugen Botschaft.

10

SIOF ist sehr ein Laufzeit-Artefakt, der Compiler und Linker haben nicht viel damit zu tun. Betrachten Sie die Funktion atexit(), sie registriert Funktionen, die beim Programm-Exit aufgerufen werden. Viele CRT-Implementierungen haben etwas Ähnliches für die Programminitialisierung, nennen wir es atinit().

Initialisierung dieser globalen Variablen erfordert Ausführungscode, der Wert kann nicht vom Compiler bestimmt werden. Daher generiert der Compiler Code-Schnipsel, die den Ausdruck ausführen und den Wert zuweisen. Diese Snippets müssen ausgeführt werden, bevor main() ausgeführt wird.

Hier kommt atinit() ins Spiel. Eine gängige CRT-Implementierung führt eine Liste von Atinit-Funktionszeigern durch und führt die Initialisierungs-Snippets der Reihe nach aus. Das Problem ist die Reihenfolge, in der die Funktionen in der Liste atinit() registriert sind. Während atexit() eine gut definierte LIFO-Reihenfolge hat und implizit durch die Reihenfolge bestimmt wird, in der der Code atexit() aufruft, ist dies bei atinit-Funktionen nicht der Fall. Die Sprachspezifikation erfordert keine Bestellung. Sie können in Ihrem Code nichts tun, um eine Bestellung anzugeben. SIOF ist das Ergebnis.

Eine mögliche Implementierung ist der Compiler, der Funktionszeiger in einem separaten Abschnitt ausgibt. Der Linker führt sie zusammen und erzeugt die endgültige Liste. Wenn Ihr Compiler das tut, wird die Reihenfolge der Initialisierung durch die Reihenfolge bestimmt, in der Sie die Objektdateien verknüpfen. Sehen Sie sich die Map-Datei an, Sie sollten den Abschnitt atinit sehen, wenn Ihr Compiler dies tut. Es wird nicht atitit aufgerufen, aber eine Art von Namen mit "init" ist wahrscheinlich. Ein Blick auf den CRT-Quellcode, der main() aufruft, sollte ebenfalls einen Einblick geben.

Verwandte Themen