2015-12-19 46 views
24

Stavo leggendo this great post about memory layout of C programs. Dice che le variabili globali inizializzate di default risiedono nello BSS segment e se si fornisce esplicitamente un valore a una variabile globale allora risiederà nello data segment.Perché i compilatori C e C++ posizionano le variabili globali inizializzate e inizializzate in modo esplicito in segmenti diversi?

Ho testato i seguenti programmi in C e C++ per esaminare questo comportamento.

#include <iostream> 
// Both i and s are having static storage duration 
int i;  // i will be kept in the BSS segment, default initialized variable, default value=0 
int s(5); // s will be kept in the data segment, explicitly initialized variable, 
int main() 
{ 
    std::cout<<&i<<' '<<&s; 
} 

uscita:

0x488020 0x478004 

Così, dalla uscita sembra chiaramente come sia variabile I & s risiede in completamente diversi segmenti. Ma se rimuovo l'inizializzatore (valore iniziale 5 in questo programma) dalla variabile S e poi eseguo il programma, mi dà l'output di sotto.

uscita:

0x488020 0x488024 

Così, dalla uscita sembra chiaramente come entrambe le variabili i e s risiede nella stessa (in questo caso BSS) segmento.

Questo comportamento è anche lo stesso in C.

#include <stdio.h> 
int i;  // i will be kept in the BSS segment, default initialized variable, default value=0 
int s=5; // s will be kept in the data segment, explicitly initialized variable, 
int main(void) 
{ 
    printf("%p %p\n",(void*)&i,(void*)&s); 
} 

uscita:

004053D0 00403004 

Così, ancora una volta si può dire, cercando in uscita (mezzi che esaminano l'indirizzo delle variabili), sia le variabili i e s risiedono in segmenti completamente diversi. Ma ancora una volta se rimuovo l'inizializzatore (valore iniziale 5 in questo programma) dalla variabile S e poi eseguo il programma mi dà l'output di sotto.

uscita:

004053D0 004053D4 

Così, dalla uscita sembra chiaramente come entrambe le variabili i e s risiede nella stessa (in questo caso BSS) segmento.

Perché i compilatori C e C++ posizionano le variabili globali inizializzate in modo esplicito e quelle predefinite di default in diversi segmenti? Perché c'è una distinzione su dove risiede la variabile globale tra le variabili inizializzate e quelle inizializzate esplicitamente? Se non sbaglio, gli standard C e C++ non parlano mai dello stack, dell'heap, del segmento dati, del segmento di codice, del segmento BSS e di tutte le cose che sono specifiche dell'implementazione. Quindi, è possibile che un'implementazione C++ memorizzi le variabili inizializzate e inizializzate in modo esplicito negli stessi segmenti invece di tenerle in segmenti diversi?

+2

[.bss] (https://en.wikipedia.org/wiki/.bss): "In genere solo la lunghezza della sezione bss, ma nessun dato, è memorizzata nel file oggetto ... I sistemi operativi possono utilizzare una tecnica chiamata zero-fill-on-demand per implementare in modo efficiente il segmento bss " –

+1

Feel con privilegi ... Una volta dovevo usare un compilatore che posizionava variabili statiche in' .data' se avevano l'inizializzatore '= 0', e in '.bss' se non avevano alcun inizializzatore. E ha anche dovuto usare la stessa base di codice su un altro compilatore (non funzionante) che non inizializzava a zero le variabili statiche senza inizializzatore. –

+0

Quiz: cosa significa BSS? –

risposta

27

Nessuna lingua C o C++ ha alcuna nozione di "segmenti", e non tutti i sistemi operativi lo fanno, quindi la tua domanda dipende inevitabilmente dalla piattaforma e dal compilatore.

Detto questo, le implementazioni comuni tratteranno diversamente le variabili inizializzate e non inizializzate. La differenza principale è che i dati non inizializzati (o 0-inizializzati) predefiniti non non devono essere effettivamente salvati con il modulo compilato, ma solo dichiarato/prenotato per l'utilizzo futuro in fase di esecuzione. In termini pratici "segmento", i dati inizializzati sono salvati su disco come parte del binario, mentre i dati non inizializzati sono non, invece è allocato all'avvio per soddisfare le "prenotazioni" dichiarate.

11

Per definizione, BSS non è un segmento diverso, è una parte del segmento di dati.

In C e C++, oggetti staticamente assegnati senza un esplicito inizializzatore sono inizializzate a zero, un'implementazione può anche assegnare variabili staticamente assegnati e costanti inizializzati con un valore costituito esclusivamente di zero valori bit il BSS section.

Un motivo per memorizzarli in BSS è, questi tipi di variabili con valori non inizializzate o di default può essere ottenuto in fase di esecuzione senza sprecare spazio nei file binari, piuttosto che le variabili che sono posti nei dati del segmento.

13

La risposta veramente breve è "perché occupa meno spazio". (Come notato da altri, il compilatore non deve farlo!)

Nel file eseguibile, la sezione data conterrà i dati che hanno il proprio valore memorizzato nella relativa posizione. Ciò significa che per ogni byte di dati inizializzati, quella sezione di dati contiene un byte.

Per le cifre globali inizializzate a zero, non c'è motivo di memorizzare molti zeri. Invece, è sufficiente memorizzare la dimensione dell'intero insieme di dati in un unico valore di dimensione. Quindi, invece di memorizzare 4132 byte di zero nel seciton data, c'è solo un "BSS è lungo 4132 byte" - e dipende dal sistema operativo/tempo di esecuzione impostarlo in modo che sia zero. - in alcuni casi, il runtime del compilatore sarà memset(BSSStart, 0, BSSSize) o simile. Ad esempio, in Linux, tutta la memoria "non utilizzata" viene riempita con zero comunque quando viene creato il processo, quindi impostare BSS su zero è solo questione di allocare la memoria in primo luogo.

E, naturalmente, i file eseguibili più brevi hanno diversi vantaggi: meno spazio occupato sul disco rigido, tempo di caricamento più veloce [bonus extra se il sistema operativo pre-riempie la memoria allocata con zero], tempo di compilazione più veloce del compilatore/linker non deve scrivere i dati sul disco.

Quindi c'è una ragione completamente pratica per questo.