2010-06-23 14 views
14

Ausgehend von einem C-Hintergrund habe ich immer angenommen, dass die POD-Typen (zB Ints) nie automatisch in C++ initialisiert wurden, aber es scheint, dass dies schlicht falsch war!Wann werden C++ POD-Typen auf Null initialisiert?

Mein Verständnis ist, dass nur "nackte" nicht-statische POD-Werte nicht mit Null gefüllt werden, wie im Code-Snippet gezeigt. Habe ich es richtig gemacht, und gibt es noch andere wichtige Fälle, die ich verpasst habe?

static int a; 

struct Foo { int a;}; 

void test() 
{ 
    int b;  
    Foo f; 
    int *c = new(int); 
    std::vector<int> d(1); 

    // At this point... 
    // a is zero 
    // f.a is zero 
    // *c is zero 
    // d[0] is zero 
    // ... BUT ... b is undefined  
} 
+0

Sind Sie sicher, dass es C++ ist und nicht, sagen wir, Ihr Betriebssystem? Ich könnte mir vorstellen (aber nicht überprüft haben), dass, wenn ein Betriebssystem Speicher zu Ihrem Prozess zuweist, würde es Null, zumindest wenn Sie die Speicherseite berührt. Weder C noch C++ erfordern dieses Verhalten, aber wenn das Betriebssystem die Speicherseiten mit dem letzten Prozess ausgibt, wäre es ein großes Sicherheitsloch. Dort könnten Anmeldeinformationen oder private Schlüssel enthalten sein, wenn ssh zum Beispiel der letzte Prozess war, der diese physische Speicherseite verwendet. –

+0

Es wäre verschwenderisch für das Betriebssystem, den Speicher bei der Zuweisung auf Null zu setzen. – Alan

+2

Sie könnten auch den ausgezeichneten, verwandten Beitrag lesen von Michael Burr in Antwort auf [Machen die Klammern nach dem Typnamen einen Unterschied mit neuen?] (Http://stackoverflow.com/questions/620137/do-the-parentheses- after-the-type-name-make-a-difference-with-new/620402 # 620402) –

Antwort

16

Vorausgesetzt, dass Sie nicht geändert haben a vor dem Aufruf test(), a hat einen Wert von Null, da Objekte mit statischer Speicherdauer beim Start des Programms initialisiert werden.

d[0] hat einen Wert von Null, da der Konstruktor, der von std::vector<int> d(1) aufgerufen wird, einen zweiten Parameter hat, der ein Standardargument verwendet; Dieses zweite Argument wird in alle Elemente des zu konstruierenden Vektors kopiert. Das Standardargument ist T(), so dass Ihr Code entspricht:

std::vector<int> d(1, int()); 

Sie sind richtig, dass b einen unbestimmten Wert hat.

f.a und *c beide haben auch unbestimmte Werte. Um Wert zu initialisieren sie (die für POD-Typen ist die gleiche wie Null Initialisierung), können Sie verwenden:

Foo f = Foo();  // You could also use Foo f((Foo())) 
int* c = new int(); // Note the parentheses 
+0

Größtenteils vereinbart; nur bin ich mir nicht sicher, warum 'Foo f' den synthetisierten Konstruktor nicht aufruft, und wenn ja, warum ist das anders als' Foo f = Foo(); '... – xtofl

+0

Danke. Und vielen Dank für den tollen Link in Ihrem Kommentar! – Roddy

+1

@xtofl: Wenn für ein POD-Objekt kein Initialisierer vorhanden ist (wie es bei 'Foo f;' der Fall ist), wird dieses Objekt nicht initialisiert. 'Foo f = Foo();' erstellt einen Wert-initialisiertes 'Foo' (das ist was der' Foo() 'Teil tut) und benutzt dann das, um' f' zu initialisieren. –

1

fällig Eigentlich können einige der Werte Null sind Sie diesen Code in der Debug-Version der Anwendung versuchen (wenn das der Fall ist).

Wenn ich nicht irre, in Ihrem Code:

  • a sollte nicht initialisiert werden.
  • b sollte
  • c nicht initialisiert werden sollte, um eine neue (nicht initialisierten) Punkt int
  • d sollte initialisiert werden [0] (wie Sie richtig erraten)
+0

Ich denke, 'a' würde heute in den meisten Unix-ähnlichen Betriebssystemen auf" 0 "gelöscht werden. Technisch gesehen wäre 'a' im' .bss'-Segment, das normalerweise auf alle 0 gesetzt wird, bevor 'main()' aufgerufen wird. –

+0

Ja, aber mein Punkt war, dass er sich nicht darauf verlassen sollte, selbst wenn der Wert im Code als Null erscheint. Explizite Initialisierung ist der Weg, hier zu gehen. – utnapistim

+1

Wenn "a" vor dem Aufruf von "test()" nicht geändert wurde, hat es den Wert Null. Objekte mit statischer Speicherdauer werden beim Start des Programms auf Null initialisiert. –

0

Für mich POD-Typen werden initialisiert je nach Teil des Speichers sie platziert werden. Ihr static int a ist für das Datensegment reserviert, so dass es beim Start einen Standardwert hat. Aber ich denke, f ist nicht in Ihrem Beispiel inizialisiert ...

0

Sie nicht. Debug-Bit-Versionen könnten dies tun, aber normalerweise wird es einfach in den Speicher gelegt und initialisiert mit dem, was zufällig der Wert im Speicher ist.

1

Beachten Sie, dass die Null-Initialisierung, die vom Betriebssystem als Sicherheitsfunktion durchgeführt wird, normalerweise erst beim ersten Speicherzugriff erfolgt. Damit meine ich jedes Segment in den Heap-, Stack- und Datenabschnitten. Die Stapel- und Datenabschnitte haben typischerweise eine feste Größe und werden initialisiert, wenn die Anwendung in den Speicher geladen wird.

Das Datensegment (mit statischen/globalen Daten und Code) wird normalerweise nicht "wiederverwendet", obwohl dies möglicherweise nicht der Fall ist, wenn Sie den Code zur Laufzeit dynamisch laden.

Der Speicher im Stack-Segment wird immer wieder verwendet. Lokale Variablen, Funktionsstapelrahmen usw. werden ständig verwendet und wiederverwendet und nicht jedes Mal initialisiert - gerade wenn die Anwendung zum ersten Mal geladen wird.

Wenn die Anwendung jedoch Anforderungen für Heap-Speicher stellt, initialisiert der Speichermanager normalerweise Speichersegmente, bevor die Anforderung erteilt wird, jedoch nur für neue Segmente. Wenn Sie eine Anforderung für Heap-Speicher stellen und in einem bereits initialisierten Segment freier Speicherplatz vorhanden ist, wird die Initialisierung nicht ein zweites Mal durchgeführt. Daher gibt es keine Garantie, dass, wenn dieses bestimmte Speichersegment von Ihrer Anwendung wiederverwendet wird, es erneut initialisiert wird.

Wenn Sie zum Beispiel ein Foo auf dem Heap zuweisen, seinem Feld einen Wert zuweisen, die Foo-Instanz löschen und dann ein neues Foo auf dem Heap erstellen, besteht die Möglichkeit, dass das neue Foo zugewiesen wird an der gleichen genauen Speicherstelle wie der alte Foo, und so wird sein Feld anfangs den gleichen Wert wie das alte Foo-Feld haben.

Wenn Sie darüber nachdenken, ist dies sinnvoll, da das Betriebssystem die Daten nur initialisiert, um zu verhindern, dass eine Anwendung auf die Daten von einer anderen Anwendung zugreift. Es besteht ein geringeres Risiko, dass eine Anwendung auf ihre eigenen Daten zugreifen kann. Aus Gründen der Performance wird die Initialisierung nicht jedes Mal durchgeführt, sondern nur dann, wenn ein bestimmtes Speichersegment für die Anwendung (in jedem Segment) verfügbar gemacht wird.

Wenn Sie eine Anwendung im Debug-Modus ausführen, initialisieren einige Debugmodus-Laufzeiten manchmal Stapel- und Heap-Daten bei jeder Zuordnung (so wird Ihr Foo-Feld immer initialisiert). Unterschiedliche Debug-Laufzeiten initialisieren die Daten jedoch auf unterschiedliche Werte. Einige Null initialisiert und einige initialisieren auf einen "Marker" -Wert.

Der Punkt ist - verwenden Sie nie irgendwo in Ihrem Code nicht initialisierte Werte. Es gibt absolut keine Garantie, dass sie null initialisiert werden. Stellen Sie außerdem sicher, dass Sie den zuvor verlinkten Artikel zu parens und der Standard-vs-Wert-Initialisierung lesen, da sich dies auf die Definition eines "nicht initialisierten" Werts auswirkt.

+0

Dort * absolut * ist eine Garantie, dass "a" im Beispiel des OPs Null initialisiert wird. –

Verwandte Themen