Variabili globali? No, Grazie

V

Variabili globali? No, Grazie perché NON usare le variabili globali nel C (e C++)

"L'unica sua dote era una specie di futile entusiasmo. In pratica sembrava che usasse l'archetto come una sega, straziando le corde al punto da condurre l'ascoltatore verso pericolosi stati di follia. Non aveva cognizione della natura dello strumento: provava a soffiarci dentro." (Mr. A.Torgman) [maestro di violoncello di Virgil Starkwell, intervistato].

In Prendi i soldi e scappa il maestro di violoncello di Virgil Starkwell ci descriveva come si sentivano gli sfortunati ascoltatori di Virgil (Woody Allen) durante le sue esibizioni. Ecco, io ho esattamente le stesse sensazioni quando leggo del codice che usa uno dei capisaldi della non-programmazione: le variabili globali. 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.

…la variabile globale: lo strumento sbagliato nel posto sbagliato…

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 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

int my_var = 0;   // globale!

// funzione che incrementa la globale
void incrementaMyVar()
{
    my_var += 5;
}

// funzione main
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;    // globale!

// funzione lunghissima che fa un sacco di cose
void faccioMilleCose()
{
    // faccio mille cose...
    // ...

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

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

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

    // chiamo faccioMilleCose()
    faccioMilleCose();  // oops! ho incrementato my_var senza accorgermene

    // 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 bisogno di spiegarlo, una globale è tutto meno che incapsulata… oops, ma questo è OOP, quindi esula un po’ l’argomento del post… ah, no: questo articolo vale sia per C che per C++ (ed anche altri linguaggi, direi…). Beh, visto che siamo in argomento OOP, e quindi C++, cito volentieri il grande M.Cline che nelle su C++FAQ dice (traduco, eh):

I nomi delle variabili globali dovrebbero iniziare con //. 
Ecco il modo ideale per dichiarare una variabile globale:  

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

Ecco il modo ideale per utilizzare una variabile globale:

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

Ok, questo è un gioco. Una specie. 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 malvagie. Quindi lavatevi le mani dopo averle usate. Due volte.
                                 (Marshall Cline C++FAQ sezione 27.15)

Sante parole.

E, per restare in tema C++, aggiungo un piccolo appunto: una Duna, anche se ci attacchi un logo Ferrari resta, ahimè, una Duna (e non me ne vogliano i lettori che usano la Duna, eh!). Allo stesso modo se, per darti delle arie, chiami Singleton una Variabile Globale Sofisticata il risultato non cambia, puoi sofisticarla quanto vuoi (inizializzazione “lazy”, inizializzazione “eager”, inizializzazione “oggi-sono-indeciso-e-faccio-un-mix-di-lazy-e-eager”, ecc., ecc.) ma, sotto sotto, è sempre una schifezza di Variabile Globale (anche se un pelino più accettabile, devo ammetterlo).

(…e qua bisogna giocare d’anticipo:  so che la affermazione qui sopra avrà fatto saltare sulla sedia tutti gli adepti della religione di Design Patterns, si, quelli che la sera recitano le orazioni davanti alla foto della Gang of Four che tengono sul comodino. Ecco, ho il massimo rispetto per loro, per la Gang of Four e per il libro che ha, sicuramente, un grande valore tecnico (e chi sono io per negarlo?) ma non è da prendere tutto come oro colato…  E vi assicuro che molta gente ha scritto del buon Software anche prima dell’uscita di quel libro (ma come avranno fatto?). Ecco, quel libro è l’equivalente informatico de Google, chiedendo, ad esempio: “global variables are evil?“, e vedrete che valanga di risultati vi verrà fuori. Se vi può interessare una delle pagine più ben fatte la trovate qui.

A questo punto 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, anche se a malincuore, bisogna farlo: la salute prima di tutto.

Ciao e al prossimo post!

A proposito di me

Aldo Abate

È un Cinefilo prestato alla Programmazione in C. Per mancanza di tempo ha rinunciato ad aprire un CineBlog (che richiede una attenzione quasi quotidiana), quindi ha deciso di dedicarsi a quello che gli riesce meglio con il poco tempo a disposizione: scrivere articoli sul suo amato C infarcendoli di citazioni Cinematografiche. Il risultato finale è ambiguo e spiazzante, ma lui conta sul fatto che il (si spera) buon contenuto tecnico induca gli appassionati di C a leggere ugualmente gli articoli ignorando la parte Cinefila.
Ma in realtà il suo obiettivo principale è che almeno un lettore su cento scopra il Cinema grazie al C...