Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

domenica 23 settembre 2012

Variabili globali? No, Grazie
come usare le variabili globali nel C

Oggi cercheremo di demolire un altro dei capisaldi della non-programmazione: la variabile globale. Intendiamoci: le variabili globali (come anche il goto, per esempio) fanno parte del linguaggio, quindi esistono: a volte è possibile e/o necessario usarle. Però, esattamente come il goto, è quasi sempre possibile farne a meno, con grandi benefici di stile (leggibilità e manutenibilità, soprattutto), e funzionalità (meno bugs): chiamalo poco.

I punti critici sono moltissimi, ma, visto che non voglio scrivere né un poema, né un libro sull'argomento, ne ho isolati alcuni. Vediamo:

1) le variabili globali non sono thread-safe: nella programmazione multithreading l'uso delle globali può generare problemi di malfunzionamento subdoli e difficili da trovare. Il caso classico sono le istruzioni di lettura/scrittura di una globale fuori da una zona critica, ossia (per esempio) senza la protezione di un mutex: in un grande progetto è sufficiente una (una sola!) dimenticanza, di questo tipo per creare tanti di quei mal di testa che ti passerà la voglia di usare le globali per il resto della tua vita di programmatore. Provare per credere. A questo punto uno potrebbe obbiettare: "ma io scrivo programmi normali, non multithreading". Va bene: premettendo (e chiarendo nel prossimo paragrafo) che un programma "normale" può essere considerato un programma multi-thread con un solo thread, ringrazio per l'obiezione, che mi serve giusto di spunto per illustrare il punto 2:

2) le variabili globali violano il principio di manutenzione/riutilizzazione del Software: quando si scrive del codice professionalmente bisogna sempre pensare al lavoro in team, e quindi alle operazioni di manutenzione che potrebbero essere svolte da altri (e, a volte) dopo molto tempo: evidentemente un codice, vasto e pieno di globali, è difficile da manutenere come un codice No Comment (ricordate ?), perché la storia di una globale è poco comprensibile, potrebbe essere toccata in molti posti diversi da molte funzioni diverse, e, se per capire un pezzetto di codice bisogna aprire alcune decine di file... avete vinto un altro bel mal di testa! E non ne parliamo di riutilizzare del codice pieno di globali per un altro progetto: se non l'avete mai fatto provate almeno a immaginarvi la difficoltà. E non solo: torniamo al punto 1 (thread-safe): chi mi dice che del codice "normale" non lo debba riutilizzare (un giorno) in un progetto multithread ? Se il codice è thread-safe si può fare agevolmente, ma se ci sono delle globali di mezzo... beh, buon lavoro (e buona fortuna).

3) le variabili globali aumentano la difficoltà del debug e moltiplicano la probabilità di errori di programmazione: per quanto riguarda il debug vi rimando al punto 2: se il valore di una variabile è difficile da seguire a livello di manutenzione lo sarà anche a livello di debug. E ci saranno anche più malfunzionamenti da debuggare (fantastico!), perché, oltre a tutti i possibili errori di codificazione ci aggiungiamo anche quelli di scope: provate questo codice:
int my_var = 0;

void incrementaMyVar()
{
    my_var += 5;
}

int main(int argc, char **argv)
{
    // faccio mille cose...
    // ...

    // incremento my_var
    incrementaMyVar();

    // faccio altre mille cose...
    // ...

    // definisco una "nuova" variabile my var e la uso
    int my_var = 2;        // ah, ah, ah: ridefinizione!
    // ...

    // faccio ancora mille cose...
    // ...

    // incremento e test my_var (oops! quale my_var?)
    incrementaMyVar();
    if (my_var == 2)
        formatMyHardDisk();    // uh, uh, uh: era quella sbagliata!
    // ...

    return 0;
}
Quello mostrato sopra era un problema di scope con ridefinizione locale (accidentale) di una globale. Il codice che segue è ancora più semplice, mostra una svista su un dettaglio importante:
int my_var = 0;

void faccioMilleCose()
{
    // faccio mille cose...
    // ...

    // incremento my_var (sepolto tra mille istruzioni!)
    my_var += 5;

    // faccio altre mille cose...
    // ...
}

int main(int argc, char **argv)
{
    // faccio un po' di cose...
    // ...

    // chiamo faccioMilleCose()
    faccioMilleCose();    // oops! ho incrementato my_var per sbaglio

    // faccio altre cose...
    // ...

    // test my_var
    if (my_var == 5)
        formatMyHardDisk();    // uh, uh, uh: ho sbagliato qualcosa?
    // ...

    return 0;
}
bella storia, eh ?

4) le variabili globali violano il principio di incapsulamento delle variabili: beh, questo non c'è nemmeno di bisogno di spiegarlo, una globale è tutto meno che incapsulata... oops, ma questo è OOP, quindi esula un po' l'argomento del blog: scusatemi, volevo strafare. Beh, visto che siamo in argomento OOP, e quindi C++, cito volentieri il grande M.Cline che nelle su C++FAQ dice (traduco, eh):
Il nome di una variabile globale deve iniziare con //.

Ecco il modo ideale di dichiarare una variabile globale:

// int xyz; <-la cosa che rende ideale questa globale è l'iniziale //

Ecco il modo ideale di usare una variabile globale:

void mycode()
{
  ...
  // fai_qualcosa_con(xyz);  <-idem come sopra
  ...
}

OK, questo è un gioco. Una specie di. La verità è che ci sono casi in
cui le variabili globali sono meno peggio delle alternative - quando
le globali sono il minore dei mali. Ma loro sono sempre malvage. 
Quindi lavatevi le mani dopo averle usate. Due volte.
Sante parole.

Comunque, se pensate che tutto questo sia solo farina del mio sacco provate a chiedere al nostro amico Google: global variable are evil? e vedrete che valanga di risultati viene fuori. Se vi interessa una delle pagine più interessanti la trovate qui.

Penso che quanto detto sia sufficiente. Evidentemente io sono un po' prevenuto perchè ho lavorato a lungo su software multithreading (e ho ancora un po' di mal di testa...), però vi chiedo di fidarvi e di propagare il messaggio il più possibile. Si, lo so, dire a un programmatore inesperto (o a un programmatore stanco) "non usare le variabili globali" è come dire a un bambino "non mangiare troppe caramelle": beh, bisogna farlo. La salute prima di tutto.

Ciao e al prossimo post.

Nessun commento:

Posta un commento