Variabile non inizializzata

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

In informatica, una variabile non inizializzata è una variabile dichiarata ma non inizializzata ad un valore noto definito prima di essere utilizzata. Avrà un certo valore, ma non è prevedibile quale. Proprio per questo è un errore di programmazione e una fonte comune di bug nel software.

Esempio in linguaggio C[modifica | modifica wikitesto]

Un presupposto comune fatto dai programmatori inesperti è che tutte le variabili siano impostate su un valore noto, come zero, quando vengono dichiarate. Anche se questo è vero per molti linguaggi, non è vero per tutti, quindi c'è il rischio di commettere questo errore. Linguaggi come il C utilizzano lo spazio riservato allo stack per le variabili, la raccolta di variabili allocate per una subroutine è nota come stack frame. Sebbene il computer riservi la quantità appropriata di spazio per lo stack frame, di solito lo fa semplicemente agendo sul valore del puntatore dello stack e non imposta la memoria stessa su alcun nuovo stato (tipicamente per motivi di efficienza). Pertanto, qualunque contenuto di quella zona di memoria in quel momento sarà il valore iniziale delle variabili che occupano quegli indirizzi.

Ecco un semplice esempio in C:

void conta(void)
{
    int k, i;
    
    for(i = 0; i < 10; i++)
    {
        k = k + 1;
    }
    
    printf("%d", k);
}

Il valore finale di k non è definito. La risposta che deve essere 10 presuppone che sia iniziato da zero, il che può essere vero o meno. Si noti che nell'esempio la variabile i viene inizializzata a zero dalla prima clausola dell'istruzione for. Un altro esempio è quando si ha a che fare con le strutture. Nel frammento di codice seguente, scritto in C, abbiamo una struct studente che contiene alcune variabili che descrivono le informazioni su uno studente. La funzione registra_studente perde memoria perché non riesce a inizializzare completamente i membri di struct studente nuovo_studente. Se diamo uno sguardo più da vicino, all'inizio vengono inizializzati eta, semestre e numero_studente. Ma l'inizializzazione dei membri nome e cognome non è corretta. Questo perché se la lunghezza degli array di caratteri nome e cognome è inferiore a 16 byte, durante la chiamata alla funzione strcpy, non riusciremo a inizializzare completamente tutti i 16 byte di memoria riservati a ciascuno di questi membri. Quindi, dopo che la funzione memcpy() ha inserito la struttura risultante in output, viene passata un po' di memoria dello stack al chiamante.

struct studente {
    unsigned int eta;
    unsigned int semestre;
    char nome[16];
    char cognome[16];
    unsigned int numero_studente;
};

int registra_studente(struct studente *output, int eta, char *nome, char *cognome)
{
    // Se uno qualsiasi di questi puntatori è NULL, fallisce.
    if(!output || !nome || !cognome)
    {
        printf("Errore!\n");
        return -1;
    }

    // Ci assicuriamo che la lunghezza delle stringhe è minore di 16 byte (incluso il carattere terminatore)
    // per evitare overflow
    if(strlen(nome) > 15 ||  strlen(cognome) > 15) {
      printf("Il nome e il cognome non possono essere più lunghi di 16 caratteri!\n");
      return -1;
    }

    // Inizializzazione dei membri
    struct studente nuovo_studente;
    nuovo_studente.eta = eta;
    nuovo_studente.semestre = 1;
    nuovo_studente.numero_studente = get_nuovo_numero_studente();
    
    strcpy(nuovo_studente.nome, nome);
    strcpy(nuovo_studente.cognome, cognome);

    // Copia della struttura risultante in output
    memcpy(output, &nuovo_studente, sizeof(struct studente));
    return 0;
}

In ogni caso, anche quando una variabile viene inizializzata implicitamente a un valore predefinito come 0, questo non è tipicamente il valore corretto. Inizializzato non significa corretto se il valore è predefinito, tuttavia l'inizializzazione predefinita a 0 è una pratica corretta per i puntatori e gli array di puntatori, poiché li rende non validi prima che vengano effettivamente inizializzati al valore corretto. In C, le variabili con durata di archiviazione statica che non sono inizializzate esplicitamente vengono inizializzate a zero (o a null, per i puntatori).[1]

Non solo le variabili non inizializzate sono una causa frequente di bug, ma questo tipo di bug è particolarmente grave perché potrebbe non essere riproducibile: ad esempio, una variabile può rimanere non inizializzata solo in qualche ramo del programma. In alcuni casi, i programmi con variabili non inizializzate possono persino superare i test del software.

Impatti[modifica | modifica wikitesto]

Le variabili non inizializzate sono bug potenti poiché possono essere sfruttati per compiere dei memory leak arbitrari, per ottenere una sovrascrittura della memoria oppure per ottenere l'esecuzione di codice arbitrario, a seconda dei casi. Quando si utilizza un software che utilizza la randomizzazione del layout dello spazio degli indirizzi (ASLR), è spesso necessario conoscere l'indirizzo di base del software in memoria. Lo sfruttamento di una variabile non inizializzata in modo da forzare il software a far trapelare un puntatore dal suo spazio degli indirizzi può essere utilizzato per bypassare l'ASLR.

Utilizzo nei vari linguaggi[modifica | modifica wikitesto]

Le variabili non inizializzate rappresentano un problema particolare in linguaggi come il linguaggio assembly, C e C ++, progettati per la programmazione di sistemi. Lo sviluppo di questi linguaggi ha comportato una filosofia di progettazione in cui i conflitti tra prestazioni e sicurezza sono stati generalmente risolti a favore delle prestazioni. Al programmatore è stato affidato l'onere di essere a conoscenza di questioni problematiche e/o pericolose come le variabili non inizializzate.

In altri linguaggi, le variabili vengono spesso inizializzate su valori noti quando vengono create. Alcuni esempi:

  • VHDL inizializza tutte le variabili standard in un valore speciale 'U'. Viene utilizzato in simulazione, per il debugging, per far sapere all'utente quando i valori iniziali di non interesse, attraverso la logica multivalore, influenzano l'output.
  • Java non dispone di variabili non inizializzate. I campi delle classi e degli oggetti che non hanno un inizializzatore esplicito e gli elementi degli array vengono automaticamente inizializzati con il valore predefinito per il loro tipo (false per booleano, 0 per tutti i tipi numerici, null per tutti i tipi di riferimento).[2] Le variabili locali in Java devono essere assegnate definitivamente prima di accedervi, altrimenti si tratta di un errore di compilazione.
  • Python inizializza le variabili locali a NULL (diverso da None) e genera UnboundLocalError quando si accede a una tale variabile prima di essere (re)inizializzata su un valore valido.
  • D inizializza tutte le variabili a meno che non sia esplicitamente specificato dal programmatore di non farlo.

Anche nei linguaggi in cui sono consentite variabili non inizializzate, molti compilatori tenteranno di identificare l'uso di variabili non inizializzate e le segnaleranno come errori in fase di compilazione.

Note[modifica | modifica wikitesto]

  1. ^ ISO/IEC 9899:TC3 (PDF), su open-std.org. URL consultato il 26 aprile 2022. Section 6.7.8, paragraph 10.
  2. ^ 4.12.5. Initial Values of Variables, su docs.oracle.com. URL consultato il 26 aprile 2022.

Voci correlate[modifica | modifica wikitesto]

Collegamenti esterni[modifica | modifica wikitesto]

  • CWE-457 Uso di variabili non inizializzate [1] .
  Portale Informatica: accedi alle voci di Wikipedia che trattano di informatica