Quanto è importante inizializzare le variabili?
L'inizializzazione corretta evita perdite di memoria o presenta vantaggi in termini di prestazioni?
Le variabili non inizializzate rendono un programma non deterministico. Ogni volta che il programma viene eseguito, potrebbe comportarsi diversamente. Le modifiche non correlate all'ambiente operativo, all'ora del giorno, alla fase lunare e alle permutazioni di tali effetti influenzano come e quando questi demoni si manifestano. Il programma può essere eseguito un milione di volte prima che il difetto si presenti, possono farlo ogni volta o eseguire un altro milione. Molti problemi vengono messi in "errori" e ignorati, o segnalazioni di difetti da parte dei clienti chiusi come "non riproducibili". Quante volte hai riavviato una macchina per risolvere un problema? Quante volte hai detto a un cliente "Mai visto succedere, fammi sapere se lo vedi di nuovo" - sperando (sapendo) pienamente bene non lo faranno!
Poiché la riproduzione di un difetto può essere quasi impossibile nell'ambiente di test, è quasi impossibile trovarla e risolverla.
Potrebbero volerci anni prima che il bug si materializzi, comunemente in codice pensato per essere affidabile e stabile. Si presume che il difetto si trovi nel codice più recente, il rintracciamento può richiedere molto più tempo. Un cambiamento nel compilatore, un interruttore del compilatore, persino l'aggiunta di una riga di codice può modificare il comportamento.
L'inizializzazione delle variabili ha un enorme vantaggio in termini di prestazioni, non solo perché un programma che funziona correttamente è infinito più veloce di uno che non lo fa, ma gli sviluppatori dedicano meno tempo a trovare e correggere i difetti che non dovrebbero esserci e più tempo a fare "reale" lavoro.
L'altro vantaggio significativo delle variabili di inizializzazione è l'autore originale del codice che deve decidere a cosa inizializzarli. Questo non è sempre un esercizio banale, e quando non è banale, può essere un'indicazione di un design scadente.
Le perdite di memoria sono un problema diverso, ma un'inadeguata inizializzazione può non solo aiutare a prevenirle, ma può anche aiutare a individuarle e trovarne la fonte - è altamente dipendente dalla lingua e questa è davvero una domanda separata che merita un'ulteriore esplorazione di quella che sono in grado di dare in questa risposta.
Modifica: in alcune lingue (ad es. C #) non è possibile utilizzare variabili non inizializzate, poiché il programma non verrà compilato, o segnalerà un errore quando viene eseguito, se fatto. Tuttavia, molte lingue con queste caratteristiche hanno interfacce con codice potenzialmente pericoloso, quindi è necessario prestare attenzione quando si utilizzano tali interfacce per non introdurre variabili non inizializzate.
L'inizializzazione di una variabile come indicato da Telastyn può impedire bug. Se la variabile è un tipo di riferimento, l'inizializzazione può impedire errori di riferimento nulli lungo la linea.
Una variabile di qualsiasi tipo con un valore predefinito non nullo occuperà un po 'di memoria per memorizzare il valore predefinito.
Il tentativo di utilizzare una variabile non inizializzata è un bug always , quindi ha senso minimizzare la probabilità che si verifichi un errore.
Probabilmente i linguaggi di programmazione dell'approccio più comuni adottati per mitigare il problema è quello di inizializzarsi automaticamente su un valore predefinito, quindi almeno se si dimentica di inizializzare una variabile, sarà qualcosa come 0
invece di qualcosa come 0x16615c4b
.
Questo risolve una grande percentuale di bug, se ti è capitato di aver bisogno di una variabile inizializzata a zero in ogni caso. Tuttavia, l'utilizzo di una variabile inizializzata su un valore errato è altrettanto grave di quella non inizializzata. In effetti, a volte può essere anche peggio, perché l'errore può essere più sottile e difficile da rilevare.
I linguaggi di programmazione funzionale risolvono questo problema non solo disabilitando i valori non inizializzati, ma impedendo del tutto la riassegnazione. Ciò elimina il problema e risulta non essere una restrizione così grave come si potrebbe pensare. Anche nei linguaggi non funzionali, se aspetti di dichiarare una variabile finché non hai un valore corretto per inizializzarla, il tuo codice tende ad essere molto più robusto.
Per quanto riguarda le prestazioni, è probabilmente trascurabile. Nella peggiore delle ipotesi, con le variabili non inizializzate, hai un compito in più e accumuli memoria più a lungo del necessario. I buoni compilatori possono ottimizzare le differenze in molti casi.
Le perdite di memoria sono completamente indipendenti, anche se le variabili correttamente inizializzate tendono ad essere nel raggio di azione per un periodo di tempo più breve, e quindi potrebbe essere un po 'meno probabile per un programmatore di perdere accidentalmente.
L'inizializzazione implica che il valore iniziale conta. Se il valore iniziale conta, allora sì, chiaramente devi assicurarti che sia inizializzato. Se non importa, ciò implica che verrà inizializzato più tardi.
L'inizializzazione non necessaria causa sprechi di cicli della CPU. Anche se questi cicli sprecati potrebbero non avere importanza in alcuni programmi, in altri programmi, ogni singolo ciclo è importante in quanto la velocità è di primaria importanza. Quindi è molto importante capire quali sono i propri obiettivi di rendimento e se le variabili devono essere inizializzate o meno.
Le perdite di memoria sono un problema completamente diverso che in genere coinvolge una funzione di allocatore di memoria per emettere e successivamente riciclare blocchi di memoria. Pensa a un ufficio postale. Vai e chiedi una cassetta postale. Ti danno uno. Ne chiedi un altro. Ti danno un altro. La regola è che quando hai finito di usare una casella di posta elettronica devi restituirla. Se ti dimentichi di restituirlo pensano ancora di averlo e la scatola non può essere riutilizzata da nessun altro. Quindi c'è una parte di memoria legata e non utilizzata, ed è quella che viene definita una perdita di memoria. Se continui a chiedere scatole a un certo punto, ti verrà a mancare la memoria. Ho semplificato troppo questo, ma questa è l'idea di base.
Come altri hanno detto, dipende dalla lingua. Ma mostrerò le mie idee Java (ed efficaci Java) sull'inizializzazione delle variabili. Questi dovrebbero essere utilizzabili per molti altri linguaggi di livello superiore.
Le variabili di classe, contrassegnate con static
in Java, sono come costanti. Queste variabili dovrebbero normalmente essere finali e inizializzate direttamente dopo la definizione usando =
o all'interno di un blocco di inizializzazione di classe static { // initialize here }
.
Come in molti linguaggi di script e di livello superiore, ai campi verrà automaticamente assegnato un valore predefinito. Per i numeri e char
questo sarà il valore zero. Per le stringhe e altri oggetti sarà null
. Ora null
è pericoloso e dovrebbe essere usato con parsimonia. Quindi questi campi dovrebbero essere impostati su un valore valido il prima possibile. Il costruttore è normalmente un posto perfetto per questo. Per assicurarti che le variabili siano impostate durante la funzione di costruzione e che non siano state modificate in seguito, puoi contrassegnarle con la parola chiave final
.
Prova a resistere all'impulso di utilizzare null
come una sorta di flag o valore speciale. È meglio ad es. includere un campo specifico per mantenere lo stato. Un campo con il nome state
che utilizza i valori di un'enumerazione State
sarebbe una buona scelta.
Poiché le modifiche ai valori dei parametri (sia che si tratti di riferimenti a oggetti o tipi di base come interi ecc.) non saranno viste dal chiamante, i parametri dovrebbero essere contrassegnati come final
. Ciò significa che i valori della variabile stessa non possono essere modificati. Nota che il valore delle istanze di oggetto mutabili può essere cambiato, il riferimento non può essere cambiato per puntare a un oggetto diverso o null
però.
Le variabili locali non vengono inizializzate automaticamente; devono essere inizializzati prima che il loro valore possa essere usato. Un metodo per assicurarsi che la variabile sia inizializzata è inizializzarli direttamente con qualche tipo di valore predefinito. Questo è comunque qualcosa che dovresti non fare. La maggior parte delle volte il valore predefinito non è un valore che ci si aspetterebbe.
È molto meglio definire la variabile solo dove serve la variabile. Se la variabile è solo per prendere un singolo valore (che è vero per la maggior parte delle variabili in un buon codice), è possibile contrassegnare la variabile final
. Ciò assicura che la variabile locale sia assegnata esattamente una volta, non zero volte o due volte. Un esempio:
public static doMethod(final int x) {
final int y; // no assignment yet, it's final so it *must* be assigned
if (x < 0) {
y = 0;
} else if (x > 0) {
y = x;
} else {
// do nothing <- error, y not assigned if x = 0
// throwing an exception here is acceptable though
}
}
Nota che molte lingue ti avviseranno se una variabile rimane non inizializzata prima dell'uso. Controlla le specifiche della lingua e i forum per vedere se non ti preoccupi inutilmente.
Non ci sono problemi con le variabili di inizializzazione.
Il problema è solo quando leggi una variabile che non è stata ancora scritta.
A seconda del compilatore e / o del tipo di variabile, l'inizializzazione viene eseguita all'avvio dell'applicazione. Oppure no.
È un uso comune non affidarsi all'inizializzazione automatica.
L'inizializzazione delle variabili (implicitamente o esplicitamente) è cruciale. Non inizializzare una variabile è sempre un errore (potrebbero essere inizializzati implicitamente, comunque. Vedi sotto). I moderni compilatori come il compilatore C # (ad esempio) trattano questo come un errore e non ti permettono di eseguire il codice. Una variabile non inizializzata è semplicemente inutile e dannosa. A meno che non si stia creando un generatore di numeri casuali, ci si aspetta da un pezzo di codice per produrre un risultato deterministico e riproducibile. Questo può essere ottenuto solo se inizi a lavorare con le variabili inizializzate.
La domanda davvero interessante è se una variabile viene inizializzata automaticamente o se devi eseguirla manualmente. Dipende dalla lingua utilizzata. Ad esempio, in C #, i campi, ad esempio "variabili" a livello di classe, vengono sempre inizializzati automaticamente sul valore predefinito per quel tipo di variabile default(T)
. Questo valore corrisponde a un pattern di bit costituito da tutti gli zeri. Questo fa parte delle specifiche del linguaggio e non solo un dettaglio tecnico dell'implementazione della lingua. Quindi puoi tranquillamente affidarti a questo. È sicuro non inizializzare una variabile esplicitamente se (e solo se) la specifica della lingua afferma che è inizializzata in modo implicito. Se vuoi un altro valore, devi inizializzare la variabile esplicitamente. Però; nelle variabili locali C #, cioè le variabili dichiarate nei metodi, non inizializzato automaticamente e devi sempre inizializzare la variabile in modo esplicito.
Leggi altre domande sui tag variables performance coding initialization