2013-01-22 13 views
15

Data la seguente funzione, ognuna delle variabili locali sarà dichiarata nello stack?Variabili condizionali stack

std::string reallyCoolFunction(unsigned int a) 
{ 
    if(a < 20) 
    { 
    std::string result1 = "This function is really cool"; 
    return result1; 
    } 

    if(a >=20 && a <= 40) 
    { 
    std::string result2 = "This function is kind of cool"; 
    return result2; 
    } 

    if(a > 40) 
    { 
    std::string result3 = "This function is moderately cool"; 
    return result3; 
    } 

std::string result4 = "This function really isn't that cool"; 
return result4; // remove warning 

} 

In questa situazione, un solo std::string è effettivamente necessaria, tutta fa 4 ottenere stanziati in pila, o venga assegnate solo 1?

risposta

14

La decisione spetta al compilatore: dal momento che i automatici variabili andare fuori del campo di applicazione prima di quello successivo arriva nel campo di applicazione, il compilatore può ri-utilizzare la loro memoria. Tieni presente che le variabili "stack" sono in realtà le variabili con durata di archiviazione automatica in base alle specifiche C++, quindi potrebbero non essere affatto nello stack.

+0

Credo ci sia un punto importante nella mia risposta che dimentichi di menzionare - il fatto che la maggior parte la stringa non verrà allocata nello stack. Penso che valga la pena di menzionare –

+0

@IvayloStrandjev Assolutamente - Ho messo in risalto la tua risposta proprio per questo motivo :) Lo storage automatico – dasblinkenlight

+0

ha garantito il comportamento dello stack. questo è ciò a cui si riferisce "lo stack". "in realtà" è in realtà una parola piuttosto priva di meningi. una variabile automatica può solo evitare di essere allocata nello stack essendo ottimizzata via sotto la regola as-if, e quindi è * come se * fosse allocata sullo stack, quindi non molto significativa per fare una grande nota a riguardo. –

10

Sulla maggior parte dei compilatori verrà assegnata una sola stringa. Tieni presente che se lo standard std::string utilizza la memoria dinamica, la maggior parte del suo contenuto verrà comunque allocata nell'heap.

+5

La stringa * may * utilizza la memoria dinamica ma non necessariamente. Si veda ad esempio l'ottimizzazione della stringa piccola nella libreria standard di MSVC. –

+0

@ArneMertz oh wow non lo sapevo. Grazie :) –

2

Dipende dal compilatore.
Se il compilatore è abbastanza intelligente da determinare in modo definitivo che è necessaria solo una stringa, emetterà il codice solo per una stringa.

Il compilatore è abbastanza intelligente?

Il modo più semplice è controllare il codice assembly generato.

Tutti o 4 vengono allocati nello stack o solo 1 viene assegnato?

essere esso 1 o 4 corde, l'oggetto stringa si trova sulla pila locale alla funzione ma la memoria per la stringa è ripartita in Freestore.

+0

* Sia 1 o 4 stringhe, l'oggetto stringa si trova nello stack locale alla funzione, ma la memoria per la stringa è allocata in freestore. * => SSO: Short String Optimization (implementato in Dirkumware e libC++) –

+0

@MatthieuM .: Vero e d'accordo. Grazie per la correzione. Giusto per essere più precisi, questo è un dettaglio di implementazione. Lo standard non obbliga * dove * lo spazio di archiviazione deve essere eseguito, ma solo alcuni comportamenti osservabili che l'implementazione di 'std :: string' deve rispettare. –

+1

Sì, un cambiamento notevole tra C++ 03 e C++ 11 è che l'ottimizzazione di gcc (Copy On Write) non è più possibile a causa di nuovi vincoli. –

0

In questo caso il compilatore può creare 4, 1, 2 o 3 variabili. Ma la maggior parte dei compilatori di cui sono a conoscenza creerebbero solo uno, o forse due, poiché il risultato4 rientra nell'intero ambito della funzione.

Ovviamente, se si fanno le cose "giuste" il compilatore può benissimo essere confuso e fare più di quanto non ne abbia assolutamente bisogno, quindi affidarsi a questo in funzionalità critiche non sarebbe una cosa particolarmente buona.

Modifica: devo aggiungere che il costruttore per std :: string deve essere eseguito solo se l'oggetto è effettivamente "utilizzato", quindi è possibile ottenere lo spazio di stack utilizzato, ma non chiamare il costruttore. Questo è importante se si fa qualcosa di simile:

void func() 
{ 
    if (something) 
    { 
     Lock myLock(&global_lock_object); // Constructor locks global_lock_object 
     ... do stuff that needs global_lock_object locked ... 
     // end of scope runs destructor of Lock that unlocks global_lock_object. 
    } 
    ... more code that takes a long time to execute but doesn't need lock. ... 
} 

Ora, se è stato eseguito il costruttore per Lock "troppo presto", e distrutto nello stesso ambito [e dovrebbe essere simmetrico], la serratura si terrà per l'intera durata della funzione, che sarebbe sbagliato.

0

Risposta breve: guarda l'assemblatore.

Risposta lunga: il compilatore può applicare controlli statici per determinare se ha bisogno di tutti i 4 o solo alcune delle variabili. Alcuni compilatori potrebbero allocare 4 diverse variabili in modalità di debug, altre no.Nella modalità di rilascio, alcuni ottimizzatori potrebbero vedere che i primi 3 sono ciascuno nel loro ambito e quindi possono essere messi nello stesso posto. Questi compilatori potrebbero quindi riservare spazio per due variabili stringa nello stack. Serve solo un po 'più di analisi per vedere che la quarta variabile in nessun caso coesiste con i primi tre, quindi alcuni ottimizzatori potrebbero mettere le restanti due variabili nello stesso posto.

Ma se il compilatore lo fa, e se lo fa ancora che in una situazione leggermente più complicata, può essere determinata solo per certo se si analizza l'output.

6

Molto probabilmente o forse (in uscita) e certamente (in Debug).

Questo è chiamato RVO: Return Value Optimization.

Il compilatore è effettivamente consentito elidere la copia interamente e crea il std::string direttamente nella fessura fornito dal chiamante. Questo è specifico per l'ABI e poiché tutte le ottimizzazioni si applicano solo se vengono soddisfatti un numero di criteri; nel tuo caso è probabile che si applicherà.

Se si desidera verificare, è possibile provare a esaminare l'output del compilatore in varie fasi della sua pipeline di traduzione/ottimizzazione; potrebbe essere difficile anche se dipende dalla tua toolchain.

+0

"Come se" regola per la vittoria. – dmckee

Problemi correlati