2010-06-23 20 views
14

Provenendo da uno sfondo C, ho sempre pensato che i tipi di POD (ad esempio ints) non fossero mai automaticamente inizializzati a zero in C++, ma sembra che fosse semplicemente sbagliato!Quando i tipi di POD C++ vengono inizializzati a zero?

La mia comprensione è che solo i valori POD non statici "nudi" non vengono riempiti a zero, come mostrato nel frammento di codice. Ho capito bene e ci sono altri casi importanti che ho perso?

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

Sei sicuro che sia C++ e non, ad esempio, il tuo sistema operativo? Immagino (ma non ho controllato) che quando un sistema operativo assegna memoria al processo, lo azzererebbe, almeno quando si tocca la pagina di memoria. Né C né C++ richiedono questo comportamento, ma se il sistema operativo ti dà delle pagine di memoria con qualunque sia l'ultimo processo inserito, sarebbe un grosso buco di sicurezza. Potrebbero esserci informazioni di accesso o chiavi private se, per esempio, ssh fosse l'ultimo processo per utilizzare quella pagina di memoria fisica. –

+0

Sembrerebbe un po 'dispendioso che il sistema operativo continui a azzerare la memoria quando viene allocata. – Alan

+2

Si potrebbe anche leggere l'eccellente post correlati da Michael Burr in risposta a [fare le parentesi dopo il nome del tipo fanno la differenza con nuova?] (Http://stackoverflow.com/questions/620137/do-the-parentheses- after-the-type-name-make-a-difference-with-new/620402 # 620402) –

risposta

16

Supponendo che non hai modificato a prima di chiamare test(), a ha un valore pari a zero, poiché gli oggetti con durata di archiviazione statica vengono inizializzati a zero all'avvio del programma.

d[0] ha un valore pari a zero, poiché il costruttore richiamato da std::vector<int> d(1) ha un secondo parametro che accetta un argomento predefinito; quel secondo argomento è copiato in tutti gli elementi del vettore che si sta costruendo. L'argomento di default è T(), in modo che il codice è equivalente a:

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

Lei ha ragione che b ha un valore indeterminato.

f.a e *c hanno entrambi valori indeterminati. Per valutarli inizializzazione (che per i tipi POD è la stessa di inizializzazione zero), è possibile utilizzare:

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

Principalmente concordato; solo io non sono sicuro del perché 'Foo f' non chiami il costruttore sintetizzato, e se lo fa, perché sarebbe diverso da' Foo f = Foo(); '... – xtofl

+0

Grazie. E doppio grazie per il link fantastico nel tuo commento! – Roddy

+1

@xtofl: se non è presente alcun inizializzatore (come nel caso di 'Foo f;') per un oggetto di tipo POD, quell'oggetto non è inizializzato. 'Foo f = Foo();' crea un valore 'Foo' inizializzato (questo è ciò che fa la parte' Foo() 'e poi lo usa per inizializzare' f'. –

1

In realtà alcuni dei valori che sono pari a zero può essere dovuto a voi provare questo codice nella versione di debug dell'applicazione (se questo è il caso).

Se non mi sbaglio, nel codice:

  • una dovrebbe essere inizializzato.
  • b dovrebbe essere inizializzata
  • c dovrebbero puntare a un nuovo (non inizializzato) int
  • d dovrebbe essere inizializzato a [0] (come avete indovinato correttamente)
+0

Penso che 'a' possa essere cancellato a' 0' nella maggior parte dei sistemi operativi Unix-like oggi. Tecnicamente, 'a' sarebbe nel segmento' .bss', che di solito viene impostato su tutti gli 0 prima che 'main()' venga chiamato. –

+0

Sì, ma il mio punto era che non doveva fare affidamento su questo anche se il valore appare come zero nel codice. L'inizializzazione esplicita è la strada da percorrere qui. – utnapistim

+1

A meno che 'a' non sia stato modificato prima di chiamare' test() ', avrà un valore pari a zero. Gli oggetti con durata di memorizzazione statica vengono inizializzati a zero all'avvio del programma. –

0

Per me, i tipi POD vengono inizializzati a seconda della parte della memoria sono collocati. Il tuo static int a è allocato sul segmento dati, quindi ha un valore predefinito all'avvio. Tuttavia, penso che f non sia inizializzato nel tuo esempio ...

0

Non lo fanno. Le versioni di debug bits potrebbero fare ciò, ma in genere è appena messo in memoria e inizializzato su qualunque cosa sia successo come valore in memoria.

1

Si noti che l'inizializzazione zero eseguita dal sistema operativo come funzionalità di sicurezza viene in genere eseguita solo alla prima allocazione della memoria. Con ciò intendo qualsiasi segmento nelle sezioni heap, stack e dati. Le sezioni stack e dati sono in genere di dimensioni fisse e vengono inizializzate quando l'applicazione viene caricata in memoria.

Il segmento di dati (che contiene dati/codice statici/globali) non viene in genere "riutilizzato", sebbene ciò potrebbe non essere il caso se si carica dinamicamente il codice in fase di runtime.

La memoria nel segmento di stack viene riutilizzata continuamente. Variabili locali, frame di stack di funzioni, ecc. Vengono costantemente utilizzati e riutilizzati e non vengono inizializzati ogni volta, proprio quando l'applicazione viene caricata per la prima volta.

Tuttavia, quando l'applicazione effettua richieste di memoria heap, il gestore di memoria tipicamente zero inizializzare segmenti di memoria prima di concedere la richiesta, ma solo per i nuovi segmenti. Se si effettua una richiesta per memoria heap e c'è spazio libero in un segmento già inizializzato, l'inizializzazione non viene eseguita una seconda volta. Pertanto, non vi è alcuna garanzia che se quel particolare segmento di memoria viene riutilizzato dall'applicazione, verrà nuovamente inizializzato a zero.

Quindi, ad esempio, se si assegna un Foo all'heap, assegnare un valore al proprio campo, eliminare l'istanza di Foo e quindi creare un nuovo Foo sull'heap, c'è la possibilità che il nuovo Foo venga assegnato nella stessa esatta locazione di memoria del vecchio Foo, e quindi il suo campo avrà inizialmente lo stesso valore del campo del vecchio Foo.

Se ci pensate, questo ha un senso, perché il sistema operativo è in fase di inizializzazione i dati solo per evitare che un'applicazione di accedere ai dati da un'altra applicazione. C'è meno rischio nel permettere ad un'applicazione di accedere ai propri dati, quindi per motivi di prestazioni l'inizializzazione non viene eseguita ogni volta - solo la prima volta che un particolare segmento di memoria è reso disponibile per l'uso dall'applicazione (in qualsiasi segmento).

A volte quando si esegue un'applicazione in modalità di debug, tuttavia, alcuni runtime della modalità di debug inizializzano i dati stack e heap ad ogni allocazione (quindi il campo Foo verrà sempre inizializzato). Tuttavia, diversi runtime di debug inizializzano i dati a valori diversi. Alcuni zero inizializzano e alcuni inizializzano con un valore di "marker".

Il punto è - mai e poi mai utilizzare i valori non inizializzati qualsiasi punto del codice. Non è assolutamente garantito che verranno azzerati. Inoltre, assicurati di leggere l'articolo precedentemente collegato relativo all'inizializzazione di parens e default vs value poiché questo influenza la definizione di un valore "non inizializzato".

+0

Ci * assolutamente * è una garanzia che 'a' nell'esempio dell'OP sarà zero inizializzato. –

Problemi correlati