Questa è una pratica eccellente .
Creando variabili all'interno dei loop, si garantisce che il loro ambito sia limitato all'interno del loop. Non può essere referenziato né chiamato al di fuori del ciclo.
Per di qua:
Se il nome della variabile è un po '"generico" (come "i"), non vi è alcun rischio di mescolarlo con un'altra variabile con lo stesso nome da qualche parte più avanti nel codice (può anche essere mitigato usando le -Wshadow
istruzioni di avviso su GCC)
Il compilatore sa che l'ambito della variabile è limitato all'interno del ciclo e quindi emetterà un messaggio di errore corretto se la variabile viene referenziata per errore altrove.
Ultimo ma non meno importante, alcune funzioni di ottimizzazione dedicate possono essere eseguite in modo più efficiente dal compilatore (soprattutto l'allocazione dei registri), poiché sa che la variabile non può essere utilizzata al di fuori del ciclo. Ad esempio, non è necessario archiviare il risultato per un riutilizzo successivo.
In breve, hai ragione a farlo.
Si noti tuttavia che la variabile non deve mantenere il suo valore tra ciascun ciclo. In tal caso, potrebbe essere necessario inizializzarlo ogni volta. Puoi anche creare un blocco più grande, comprendente il ciclo, il cui unico scopo è dichiarare variabili che devono conservare il loro valore da un ciclo all'altro. Ciò include in genere il contatore di loop stesso.
{
int i, retainValue;
for (i=0; i<N; i++)
{
int tmpValue;
/* tmpValue is uninitialized */
/* retainValue still has its previous value from previous loop */
/* Do some stuff here */
}
/* Here, retainValue is still valid; tmpValue no longer */
}
Per la domanda n. 2: la variabile viene allocata una volta, quando viene chiamata la funzione. In effetti, dal punto di vista dell'allocazione, è (quasi) lo stesso che dichiarare la variabile all'inizio della funzione. L'unica differenza è l'ambito: la variabile non può essere utilizzata al di fuori del ciclo. Potrebbe anche essere possibile che la variabile non sia allocata, semplicemente riutilizzando alcuni slot liberi (da altre variabili il cui ambito è terminato).
Con un ambito limitato e più preciso arrivano ottimizzazioni più accurate. Ma soprattutto, rende il tuo codice più sicuro, con meno stati (cioè variabili) di cui preoccuparti quando leggi altre parti del codice.
Questo è vero anche al di fuori di un if(){...}
blocco. In genere, invece di:
int result;
(...)
result = f1();
if (result) then { (...) }
(...)
result = f2();
if (result) then { (...) }
è più sicuro scrivere:
(...)
{
int const result = f1();
if (result) then { (...) }
}
(...)
{
int const result = f2();
if (result) then { (...) }
}
La differenza può sembrare minore, specialmente su un esempio così piccolo. Ma su una base di codice più grande, vi aiuterà: ora non v'è alcun rischio per il trasporto di un certo result
valore da f1()
al f2()
blocco. Ognuno result
è strettamente limitato al proprio scopo, rendendo il suo ruolo più preciso. Dal punto di vista del recensore, è molto più bello, dal momento che ha variabili di stato a lungo raggio di cui preoccuparsi e tenere traccia.
Anche il compilatore aiuterà meglio: supponendo che, in futuro, dopo qualche errata modifica del codice, result
non verrà correttamente inizializzato con f2()
. La seconda versione si rifiuterà semplicemente di funzionare, dichiarando un chiaro messaggio di errore in fase di compilazione (molto meglio del tempo di esecuzione). La prima versione non individuerà nulla, il risultato f1()
sarà semplicemente testato una seconda volta, confuso per il risultato di f2()
.
Informazioni complementari
Lo strumento open source CppCheck (uno strumento di analisi statica per il codice C / C ++) fornisce alcuni suggerimenti eccellenti riguardo l'ambito ottimale delle variabili.
In risposta al commento sull'allocazione: la regola sopra è vera in C, ma potrebbe non essere valida per alcune classi C ++.
Per tipi e strutture standard, la dimensione della variabile è nota al momento della compilazione. Non esiste una cosa come "costruzione" in C, quindi lo spazio per la variabile verrà semplicemente allocato nello stack (senza alcuna inizializzazione), quando viene chiamata la funzione. Ecco perché c'è un costo "zero" quando si dichiara la variabile all'interno di un ciclo.
Tuttavia, per le classi C ++, c'è questa cosa del costruttore che conosco molto meno. Immagino che l'allocazione non sarà probabilmente il problema, dal momento che il compilatore sarà abbastanza intelligente da riutilizzare lo stesso spazio, ma è probabile che l'inizializzazione avvenga ad ogni iterazione di loop.