Domanda

Apparentemente c'è molta varietà di opinioni là fuori, che vanno da " Mai! Incapsula sempre (anche se si tratta di una semplice macro!) " a " Non è un grosso problema: usali quando è più conveniente che no. "

.

Ragioni specifiche e concrete (preferibilmente con un esempio)

  • Perché le variabili globali sono pericolose
  • Quando le variabili globali dovrebbero essere usate al posto delle alternative
  • Quali alternative esistono per coloro che sono tentati di usare le variabili globali in modo inappropriato

Anche se questo è soggettivo, sceglierò una risposta (che per me rappresenta al meglio la relazione amore / odio che ogni sviluppatore dovrebbe avere con i globali) e la comunità voterà la loro proprio sotto.

Credo che sia importante per i neofiti avere questo tipo di riferimento, ma per favore non confonderlo se esiste un'altra risposta sostanzialmente simile alla tua - aggiungi un commento o modifica la risposta di qualcun altro.

-Adam

È stato utile?

Soluzione

Le variabili dovrebbero sempre avere l'ambito più piccolo possibile. L'argomento alla base di ciò è che ogni volta che si aumenta l'ambito si ha più codice che potenzialmente modifica la variabile, quindi nella soluzione viene indotta una maggiore complessità.

È quindi chiaro che evitare l'utilizzo di variabili globali è preferito se la progettazione e l'implementazione lo consentono naturalmente. Per questo motivo preferisco non utilizzare le variabili globali a meno che non siano realmente necessarie.

Non posso essere d'accordo con " mai " sia, come ogni altro concetto, le variabili globali sono uno strumento che dovrebbe essere usato quando necessario. Preferirei usare le variabili globali piuttosto che usare alcuni costrutti artificiali (come passare puntatori in giro) che maschererebbero solo il vero intento. Buoni esempi in cui vengono utilizzate le variabili globali sono le implementazioni del modello singleton o l'accesso al registro nei sistemi incorporati.

Su come rilevare effettivamente un uso eccessivo di variabili globali: ispezione, ispezione, ispezione. Ogni volta che vedo una variabile globale, devo chiedermi: è VERAMENTE necessario in un ambito globale?

Altri suggerimenti

L'unico modo per far funzionare le variabili globali è dare loro nomi che assicurino che siano univoci.

Quel nome di solito ha un prefisso associato ad alcuni "moduli" o raccolta di funzioni per le quali la variabile globale è particolarmente focalizzata o significativa.

Ciò significa che la variabile " appartiene " a quelle funzioni - fa parte di esse. In effetti, il globale di solito può essere "avvolto". con una piccola funzione che si accompagna alle altre funzioni - nello stesso file .h prefisso dello stesso nome.

Bonus.

Quando lo fai, all'improvviso, non è più realmente globale. Ora fa parte di alcuni moduli di funzioni correlate.

Questo sempre può essere fatto. Con un po 'di pensiero ogni variabile precedentemente globale può essere assegnata a una raccolta di funzioni, allocata a un file .h specifico e isolata con funzioni che consentono di modificare la variabile senza interrompere nulla.

Invece di dire "non usare mai le variabili globali", puoi dire "assegnare le responsabilità della variabile globale a qualche modulo dove ha più senso."

Considera questo koan: " se l'ambito è abbastanza stretto, tutto è globale " ;.

È ancora molto possibile in questa epoca dover scrivere un programma di utilità molto veloce per fare un lavoro una tantum.

In questi casi, l'energia necessaria per creare un accesso sicuro alle variabili è maggiore dell'energia risparmiata dai problemi di debug in una utility così piccola.

Questo è l'unico caso a cui riesco a pensare con disinvoltura dove le variabili globali sono sagge ed è relativamente raro. Programmi nuovi e utili così piccoli da poter essere completamente conservati nella memoria a breve termine del cervello sono sempre più rari, ma esistono ancora.

In effetti, potrei affermare coraggiosamente che se il programma non è così piccolo, le variabili globali dovrebbero essere illegali.

  • Se la variabile non cambierà mai, allora è una costante, non una variabile.
  • Se la variabile richiede l'accesso universale, allora dovrebbero esistere due subroutine per ottenerla e impostarla, e dovrebbero essere sincronizzati.
  • Se il programma inizia in piccolo e potrebbe essere più grande in un secondo momento, allora codifica come se il programma fosse grande oggi e abolisce le variabili globali. Non tutti i programmi cresceranno! (Anche se, naturalmente, ciò presuppone che il programmatore sia disposto a buttare via il codice a volte.)

Le variabili globali in C sono utili per rendere il codice più leggibile se una variabile è richiesta da più metodi (anziché passare la variabile in ciascun metodo). Tuttavia, sono pericolosi perché tutte le posizioni hanno la possibilità di modificare quella variabile, rendendo potenzialmente difficile rintracciare i bug. Se è necessario utilizzare una variabile globale, assicurarsi sempre che sia modificata direttamente solo da un metodo e che tutti gli altri chiamanti lo utilizzino. Ciò renderà molto più semplice il debug di problemi relativi alle modifiche in quella variabile.

Quando non sei preoccupato per il codice thread-safe : usali ovunque abbia senso, in altre parole ovunque abbia senso esprimere qualcosa come uno stato globale.

Quando il tuo codice può essere multi-thread : evita a tutti i costi. Variabili globali astratte in code di lavoro o altre strutture thread-safe o, se assolutamente necessario, avvolgerle in blocchi, tenendo presente che si tratta probabilmente di colli di bottiglia nel programma.

Vengo dal "mai" mai campo, fino a quando ho iniziato a lavorare nel settore della difesa. Esistono alcuni standard del settore che richiedono che il software utilizzi le variabili globali anziché la memoria dinamica (malloc nel caso C). Devo ripensare il mio approccio all'allocazione dinamica della memoria per alcuni dei progetti su cui lavoro. Se riesci a proteggere " globale " memoria con i semafori, i thread, ecc. appropriati, quindi questo può essere un approccio accettabile alla gestione della memoria.

La complessità del codice non è l'unica ottimizzazione della preoccupazione. Per molte applicazioni, l'ottimizzazione delle prestazioni ha una priorità molto maggiore. Ma soprattutto, l'uso di variabili globali può RIDURRE drasticamente la complessità del codice in molte situazioni. Esistono molte situazioni, forse specializzate, in cui le variabili globali non sono solo una soluzione accettabile, ma preferite. Il mio esempio specializzato preferito è il loro uso per fornire comunicazione tra il thread principale di un'applicazione con una funzione di callback audio in esecuzione in un thread in tempo reale.

È fuorviante suggerire che le variabili globali sono una responsabilità nelle applicazioni multi-thread in quanto QUALSIASI variabile, indipendentemente dall'ambito, è una potenziale responsabilità se è esposta al cambiamento su più di un thread.

Usa le variabili globali con parsimonia. Le strutture di dati dovrebbero essere utilizzate quando possibile per organizzare e isolare l'uso dello spazio dei nomi globale.

L'ambito variabile offre ai programmatori una protezione molto utile, ma può avere un costo. Stasera sono venuto a scrivere delle variabili globali perché sono un programmatore esperto di Objective-C che spesso è frustrato dalle barriere che orientano gli oggetti nell'accesso ai dati. Direi che lo zelotismo anti-globale proviene principalmente da programmatori più giovani e ricchi di teoria, esperti principalmente con API orientate agli oggetti in isolamento senza una profonda esperienza pratica delle API a livello di sistema e della loro interazione nello sviluppo di applicazioni. Ma devo ammettere che mi sento frustrato quando i venditori usano lo spazio dei nomi sciatto. Diverse distribuzioni Linux avevano " PI " e "TWOPI" predefinito a livello globale, ad esempio, che ha rotto gran parte del mio codice personale.

Devi considerare in quale contesto verrà utilizzata anche la variabile globale. In futuro vorrai duplicare questo codice.

Ad esempio se si utilizza un socket all'interno del sistema per accedere a una risorsa. In futuro vorresti accedere a più di una di queste risorse, se la risposta è sì, starei lontano dai globuli in primo luogo, quindi non sarà richiesto un grande refactor.

È uno strumento come qualsiasi altro di solito abusato ma non penso che siano malvagi.

Ad esempio, ho un programma che funziona davvero come un database online. I dati sono archiviati in memoria ma altri programmi possono manipolarli. Esistono routine interne che agiscono in modo simile alle procedure memorizzate e ai trigger in un database.

Questo programma ha centinaia di variabili globali ma se ci pensate che cos'è un database ma un numero enorme di variabili globali.

Questo programma è in uso da circa dieci anni attraverso molte versioni e non è mai stato un problema e lo farei di nuovo in un minuto.

Devo ammettere che in questo caso le variabili globali sono oggetti che hanno metodi usati per cambiare lo stato dell'oggetto. Quindi rintracciare chi ha cambiato l'oggetto durante il debug non è un problema poiché posso sempre impostare un punto di interruzione sulla routine che cambia lo stato dell'oggetto. O ancora più semplice accendo la registrazione integrata che registra le modifiche.

Le variabili globali dovrebbero essere utilizzate quando più funzioni devono accedere ai dati o scrivere su un oggetto. Ad esempio, se è necessario passare dati o un riferimento a più funzioni come un singolo file di registro, un pool di connessioni o un riferimento hardware a cui è necessario accedere attraverso l'applicazione. Questo impedisce dichiarazioni di funzioni molto lunghe e grandi allocazioni di dati duplicati.

In genere non utilizzare le variabili globali a meno che non sia assolutamente necessario poiché le variabili globali vengono ripulite solo quando viene esplicitamente detto di farlo o il programma termina. Se si esegue un'applicazione multi-thread, più funzioni possono scrivere contemporaneamente sulla variabile. Se hai un bug, rintracciarlo può essere più difficile perché non sai quale funzione sta cambiando la variabile. Si incontra anche il problema dei conflitti di denominazione a meno che non si utilizzi una convenzione di denominazione che assegna esplicitamente alle variabili globali un nome univoco.

Quando si dichiarano le costanti.

Posso pensare a diversi motivi:

scopi di debug / test (avviso - non ho testato questo codice):

#include <stdio.h>
#define MAX_INPUT 46
int runs=0;
int fib1(int n){
    ++runs;
    return n>2?fib1(n-1)+fib1(n-2):1;
};
int fib2(int n,int *cache,int *len){
    ++runs;
    if(n<=2){
        if(*len==2)
            return 1;
        *len=2;
        return cache[0]=cache[1]=1;
    }else if(*len>=n)
        return cache[n-1];
    else{
        if(*len!=n-1)
            fib2(n-1,cache,len);
        *len=n;
        return cache[n-1]=cache[n-2]+cache[n-3];
    };
};
int main(){
    int n;
    int cache[MAX_INPUT];
    int len=0;
    scanf("%i",&n);
    if(!n||n>MAX_INPUT)
        return 0;
    printf("fib1(%i)==%i",n,fib1(n));
    printf(", %i run(s)\n",runs);
    runs=0;
    printf("fib2(%i)==%i",n,fib2(n,&cache,&len));
    printf(", %i run(s)\n",runs);
    main();
};

Ho usato variabili con ambito per fib2, ma questo è un altro scenario in cui i globi potrebbero essere utili (funzioni matematiche pure che devono archiviare i dati per evitare di prenderli per sempre).

programmi usati una sola volta (ad es. per un concorso) o quando i tempi di sviluppo devono essere ridotti

i globuli sono utili come costanti tipizzate, dove una funzione da qualche parte richiede * int anziché int.

In genere evito i globi se intendo usare il programma per più di un giorno.

  • Quando non usare: Le variabili globali sono pericolose perché l'unico modo per sapere come è cambiata la variabile globale è di tracciare l'intero codice sorgente all'interno del file .c all'interno del quale sono dichiarate (o , tutti i file .c se è anche esterno). Se il tuo codice è difettoso, devi cercare nell'intero file sorgente per vedere quali funzioni lo cambiano e quando. È un incubo il debug quando va storto. Spesso diamo per scontato l'ingegnosità che sta dietro il concetto di variabili locali che escono con garbo dal campo di applicazione - è facile da rintracciare
  • Quando utilizzare: le variabili globali dovrebbero essere utilizzate quando il suo utilizzo non è eccessivamente mascherato e dove il costo dell'uso delle variabili locali è eccessivamente complesso al punto da compromettere la leggibilità. Con questo intendo la necessità di dover aggiungere un parametro aggiuntivo per far funzionare argomenti e ritorni e passare puntatori, tra le altre cose. Tre esempi classici: quando uso il pop e il push stack, questo è condiviso tra le funzioni. Ovviamente potrei usare le variabili locali ma poi dovrei passare i puntatori come parametro aggiuntivo. Il secondo esempio classico si trova in "The C Programming Language" di K & amp; R " dove definiscono le funzioni getch () e ungetch () che condividono un array di buffer di caratteri globali. Ancora una volta, non è necessario renderlo globale, ma vale la complessità aggiunta quando è piuttosto difficile confondere l'uso del buffer? Il terzo esempio è qualcosa che troverai nello spazio incorporato tra gli appassionati di Arduino. Molte funzioni all'interno della funzione principale loop condividono tutte la funzione millis () che è il tempo istantaneo di invocazione della funzione. Poiché la velocità di clock non è infinita, millis () differirà all'interno di un singolo loop. Per renderlo costante, scatta un'istantanea del tempo precedente ad ogni ciclo e salvalo in una variabile globale. L'istantanea del tempo sarà ora la stessa di quando acceduta da molte funzioni.
  • Alternative: non molto. Attenersi il più possibile all'ambito locale, specialmente all'inizio del progetto, piuttosto che viceversa. Man mano che il progetto cresce e se ritieni che la complessità possa essere ridotta utilizzando le variabili globali, fallo, ma solo se soddisfa i requisiti del punto due. E ricorda, usare l'ambito locale e avere un codice più complicato è il male minore rispetto all'uso irresponsabile delle variabili globali.

Sono in " mai " accamparsi qui; se hai bisogno di una variabile globale, utilizza almeno un modello singleton . In questo modo, raccogli i vantaggi dell'istanza pigra e non ingombri lo spazio dei nomi globale.

Le costanti globali sono utili: ottieni una maggiore sicurezza dei tipi rispetto alle macro pre-processore ed è comunque altrettanto semplice modificare il valore se decidi di doverlo fare

Le variabili globali hanno alcuni usi, ad esempio se il funzionamento di molte parti di un programma dipende da uno stato particolare nella macchina a stati. Fintanto che limiti il ??numero di posti che possono MODIFICARE la variabile che rintraccia i bug che la coinvolgono non è poi così male.

Le variabili globali diventano pericolose non appena si crea più di un thread. In tal caso dovresti davvero limitare l'ambito a (al massimo) un file globale (dichiarandolo statico) e metodi getter / setter che lo proteggono da accessi multipli laddove ciò potrebbe essere pericoloso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top