@msc gibt eine gute Einführung in die Regeln hinter diesem Verhalten.
Ich habe festgestellt, dass der Compiler nicht einmal eine Warnung ausgibt, wenn ich eine globale Variable mehrmals deklariere.
C hat drei Arten von globalen Deklarationen für Objekte, nämlich diejenigen, die es sind (und ich beschönige static
hier):
- Erklärungen, die keine Definitionen sind -
extern int a;
- Erklärungen, die auch Definitionen sind -
int a = 3;
oderextern int a = 3;
- vorläufige Definitionen -
int a;
Es sind mehrere Deklarationen vom Typ 1 und 3 zulässig, während höchstens eine (Typ 2) Definition zulässig ist.
Was ist die Erklärung für dieses Verhalten?
Wenn Sie auch nach der Motivation für diese Regeln fragen, wird eine separate Kompilierung unterstützt . (Siehe Übersetzungseinheit ).
Um ein Programm in mehrere Dateien separat kompilierten zu brechen, müssen wir ein paar Features, nämlich (a) die Lage zu , ohne notwendigerweise die Definition zu erklären , und (b) nach vorn Erklärung .
Innerhalb einer Übersetzungseinheit müssen wir in der Lage sein, auf globale Funktionen und Daten in einer anderen Übersetzungseinheit zu verweisen. Außerdem möchten wir hier eine Fehlerprüfung durchführen, um fehlende Definitionen und fehlerhafte doppelte Definitionen zu ermitteln.
Manchmal deklarieren wir in derselben Übersetzungseinheit eine globale und definieren sie später. Dies kann passieren, wenn wir aus irgendeinem Grund eine Vorwärtsdeklaration benötigen oder wenn wir eine gemeinsame Header-Datei (die Deklarationen bereitstellt) innerhalb einer Übersetzungseinheit verwenden, die auch explizite Definitionen bietet.
Da die separate Kompilierung in C durch Verknüpfung globaler Funktionen und Daten erfolgt, sind diese Funktionen auf globaler Ebene erforderlich, jedoch nicht auf lokaler Ebene.
Wie @msc hervorhebt, ist dies für lokale Variablen nicht erforderlich, da sie keine Verknüpfung haben.
C (wie viele andere Sprachen) bietet keine Verknüpfung für lokale Variablen, da die Sprache nicht versucht, eine einzelne Funktion zu unterstützen, die mehrere separate Übersetzungseinheiten umfasst.
(Natürlich kann eine Funktion mehrere Quelldateien umfassen, jedoch nicht mehrere Übersetzungseinheiten.)
Eine vorläufige Definition funktioniert genau wie eine Deklaration, da sie in mehreren Übersetzungseinheiten zulässig ist (und sich auch gut mit anderen Deklarationen kombinieren lässt). Wenn es jedoch im gesamten Programm keine (nicht vorläufige) Definition für den Bezeichner gibt, wird der Satz von (einer oder mehreren) vorläufigen Definitionen über mehrere Übersetzungseinheiten (für einen Bezeichner) als Definition für das Objekt verwendet, dessen Initialisierer ist Null.
Dies kann implementiert werden, indem diese mit der richtigen Größe und Ausrichtung in den Abschnitt .BSS eingefügt werden . Der Linker passt sie entweder an die wahre Definition an, wenn sie gefunden werden, oder passt sie aneinander an, sodass sie in BSS auf Null gesetzt werden.
Der Begriff der getrennten Zusammenstellung kann vollständig ohne das Merkmal vorläufiger Definitionen unterstützt werden - ich denke, vorläufige Definitionen gibt es hauptsächlich aus historischen Gründen. (Ich sage nicht, dass sie nicht nützlich sind, nur wenn die Sprache heute erstellt wurde, könnte dies als unnötig angesehen werden und daher nicht angeboten werden.)