2014-09-09 19 views
7

C++ Standardinitialisierung löscht nicht Variablen mit automatischem Speicher, warum die spezielle Behandlung für statische Speichervariablen?Warum Dateibereich statische Variablen müssen initialisiert werden?

War es etwas, das durch C und C++ definiert wurde, nur kompatibel sein müssen? Wenn das der Fall ist, entscheidet sich C für Null-Initialisierung?

Wenn ein Dateibereich statische Variablen mit einem Initialisierer bereitgestellt wird, werden sie zuerst initialisiert und dann wieder konstant/dynamisch initialisiert. Ist das nicht überflüssig? Zum Beispiel ist der folgende Code aus cppreference: http://en.cppreference.com/w/cpp/language/zero_initialization

#include <string> 

double f[3]; // zero-initialized to three 0.0's 
int* p; // zero-initialized to null pointer value 
std::string s; // zero-initialized to indeterminate value 
       // then default-initialized to "" 
int main(int argc, char* argv[]) 
{ 
    static int n = argc; // zero-initialized to 0 
         // then copy-initialized to argc 
    delete p; // safe to delete a null pointer 
} 

In diesem Fall, warum n nicht auf argc direkt initialisiert werden?

EDIT: Ein Teil dieser Frage die Frage hier beantwortet wurde: Static variable initialization? Aber ich glaube nicht, dass es ein Duplikat ist, weil die Antworten in der anderen Frage nicht meine zweite Frage beantworten, dh. warum die 2-stufige Initialisierung. Übrigens sagt der Titel des anderen Beitrags nicht wirklich, was genau die Frage ist.

+0

Siehe auch http://www.youtube.com/watch?v=48kP_Ssg2eY es ist ein Video von einer D-Konferenz, aber über C++ und über dieses Thema. – johannes

+0

+1 auf dem Nicht-Duplikat. Wir versuchen herauszufinden, warum wir einen Valgrind-Befund für eine Variable erfassen, die im Dateibereich mit einem Anfangswert von 0 oder false deklariert ist. Die doppelte Initialisierung, die Sie diskutieren, kann die Ursache sein. Wir haben es in die Valgrind-Liste unter [Uninitialized access findings für nicht statische Dateibereichsvariablen, die initialisiert wurden?] Verschoben (https://sourceforge.net/p/valgrind/mailman/message/34369193/). – jww

+0

Plus, das zitierte Duplikat ist eine Art von beschissen, offene Frage. Es fragt nach statischen Variablen in Java, C und C++. Die Antworten haben nicht den halben Wert von @ TonyDs Antwort unten. – jww

Antwort

12

Das Verhalten auf den Betriebssystemen, auf denen C entwickelt wurde, hat diese Standardvorgaben geprägt. Wenn Anwendungen geladen werden, stellt das Betriebssystemladeprogramm Speicherplatz für das bereit. Es ist wünschenswert, dass es auf Nullen zurückgesetzt wird. Wenn ein anderer Prozess diesen Speicher früher verwendet hat, könnte das Programm, das Sie starten, den Speicherinhalt des vorherigen Prozesses durchsuchen und möglicherweise Passwörter, Konversationen oder andere Daten sehen. Nicht jedes frühe oder einfache Betriebssystem kümmert sich darum, aber die meisten tun es, also ist die Initialisierung meistens "frei", da das Betriebssystem ohnehin eine Aufgabe ist.

Mit diesem Standardwert von 0 wird es für die Implementierung einfacher, sich auf Flags zu beziehen, die während der dynamischen Initialisierung gesetzt wurden, da es kein nicht initialisiertes Speicherlesen und folglich undefiniertes Verhalten gibt. Zum Beispiel gegeben ...

void f() { static int n = g(); } 

... der Compiler/Implementierung so etwas wie eine static bool __f_statics_initialised Variable auch implizit hinzufügen kann - was „zum Glück“ standardmäßig 0/false aufgrund der Nullung Verhalten - zusammen mit Initialisierungscode verwandt (eine möglicherweise sichere Version Gewinde) ...

if (!__f_statics_initialised) 
{ 
    n = g(); 
    __f_statics_initialised = true; 
} 

für das obige Szenario wird die Initialisierung beim ersten Aufruf gemacht, aber für globale Variablen ist es in einer nicht näher pro-Objekt Ordnung getan, irgendwann vor main() ist aufgerufen. Wenn ein objektindividueller Initialisierungscode und eine dynamische Initialisierung erforderlich sind, um Statik im nicht initialisierten Zustand von den bekannten unterscheiden zu können, ist es in diesem Szenario einfacher, robusten Startcode zu schreiben. Zum Beispiel können Funktionen prüfen, ob ein nicht-lokaler statischer Zeiger immer noch 0 ist, und new ein Objekt dafür.

Es ist auch bemerkenswert, dass viele CPUs hocheffiziente Anweisungen haben, um große Speicherbereiche auf Null zu setzen.

+0

+1 das sollte der wahre Grund sein. –

+0

Danke, also Null-Initialisierung bedeutet eigentlich 0 auf Byte-Ebene, unabhängig von der Art zu setzen? – swang

+0

Es gibt nichts in C oder C++, das eine .bss benötigt. Die Sprachen erfordern nicht einmal ein Betriebssystem: Die statische Initialisierung erfolgt auf die gleiche Weise, unabhängig davon, ob das System ein gehostetes oder freistehendes System ist. – Lundin

7

Zero-Initialisierung von Globals kommt "kostenlos", da der Speicher für sie im Segment "BSS" vor dem Start von main() zugewiesen wird. Das heißt, wenn Sie auf Ihren Zeiger p zugreifen, muss der Zeiger selbst irgendwo gespeichert werden, und das ist irgendwo tatsächlich ein bestimmter Teil der Bits in BSS. Da es zu etwas initialisiert werden muss, warum nicht null?

Nun, warum nicht automatische/Stack-Variablen dies tun? Weil das Zeit kosten würde: Die Zuweisung auf dem Stapel ist nichts anderes als den Stapelzeiger zu erhöhen (oder zu dekrementieren, eine Frage der Perspektive). Welcher Müll dort auch war, kann dort gelassen werden (gemäß C). Da wir null-init nicht kostenlos erhalten können, bekommen wir es überhaupt nicht (weil es wieder C ist, wo wir nicht gerne für Dinge bezahlen, die wir nicht benutzen).

Default-Initialisierung eines std :: string oder anderen Klassen-Typ ist ein bisschen komplexer: C++ erfordert, dass es irgendwie initialisiert wird, und der Standardkonstruktor ist natürlich derjenige, der verwendet wird, und ja, technisch ist es Null - zuerst initialisiert, aber wie besprochen, dass Zero-Init "kostenlos" passiert ist. Es könnte für eine Implementierung zulässig sein, die std :: string ausreichend analysieren kann, um zur Erstellungszeit zu bestimmen, wie ihre Bits zu initialisieren sind, als ob der Standardkonstruktor aufgerufen würde, aber ich weiß nicht, ob irgendeine Implementierung das tut.

+2

'.bss' belegt nicht wirklich Speicherplatz auf der Festplatte (was wäre der Punkt, da es alles null ist?); Es beschreibt nur einen Speicherbereich, innerhalb dessen Offsets zugewiesen werden können. – ecatmur

+0

Danke, aber warum im obigen Beispiel wird Variable n von zwei Stufen initialisiert, anstatt von einem? Zur Kompilierzeit sollte der Compiler den Wert nicht herausfinden, den wir wollen, ist argc und speichert das in BSS und überspringt die Null-Aus-Stufe. – swang

+0

@swang: Unter der "als-ob" -Regel könnte es in einer einzigen Stufe initialisiert werden, da es im Null-initialisierten Zustand nichts zu beobachten gibt. Es muss sich einfach so verhalten, als ob die Initialisierung den Regeln der Norm entspräche. (Aber es konnte den Wert in BSS nicht speichern, da dieser immer Null initialisiert ist und er konnte ihn nicht in einem statischen Datenabschnitt speichern, da der Wert von 'argc' nur zur Laufzeit bekannt ist.) –

3

Globale und statische Variablen in C haben während der Laufzeit des Programms eine feste Speicheradresse. Dies ermöglicht dem Programm-Launcher, sie zu initialisieren, indem ein geeigneter Speicherbereich aus der ausführbaren Datei in den Computerspeicher kopiert wird.

Folglich kann (muss) C für jede statische/globale Variable einen Anfangswert bereitstellen. Wenn der Benutzer keinen Wert angibt, wird standardmäßig 0 verwendet. Im Gegensatz zur lokalen Variablen erhöht dies weder den Speicher noch die Geschwindigkeit der Anwendung (da ein Wert trotzdem geschrieben werden muss).

Dieses Verhalten (statische Ausgangsdaten in ausführbare Datei kopieren) kann sehr schlecht sein, wenn Sie große Arrays ohne Anfangsdaten haben. Tatsächlich scheint es, dass moderne C-Compiler in der Lage sind, diese Verschwendung zu vermeiden, und werden große Arrays mit Nullen auffüllen, anstatt Nullen im Programm zu speichern. Wenn die Regel jedoch einmal vorliegt, sind sie gezwungen, die Region zu füllen, auch wenn der Benutzer sie möglicherweise nicht benötigt. Jedenfalls ist dies eine sehr billige Operation, die einmal beim Programmstart durchgeführt wird.

+0

Guter Punkt über die Arrays, sonst wenn ich eine statische Int [10000000000] deklarieren könnte es die ausführbare Datei extrem groß machen. – swang

Verwandte Themen