Le variabili "statiche" del campo di applicazione sono in C malvagie come le variabili globali "extern"?

7

In C, spesso / a volte (come questione di stile) si utilizza una variabile% scope di tipo file-scope dove si usa una variabile membro privata della classe in C ++. Quando si ridimensiona a programmi con multithreading, è sufficiente aggiungere static in C11 o l'estensione long support thread_local si adatta bene. So che puoi fare esattamente lo stesso in C come in C ++ mettendo tutto all'interno di __thread e creando un set di funzioni che accetta un puntatore a quel struct come primo argomento. Alcune librerie lo fanno ampiamente. Ma il mio stile personale è di mantenere un struct il più piccolo possibile, se necessario.

Spesso leggo o sento alcune persone che sostengono che le variabili "globali" sono così dannose. Seguo le loro ragioni e la maggior parte della loro argomentazione sembra essere correlata alle variabili globali struct in termini C. Quello che dicono è certamente vero. A volte uso 1 o 2% di% di dichiarazioni dichiarate di variabili nell'intero programma quando semplifica molto le cose e quando è facile tenerne traccia, ma andare più avanti renderà imprevedibile un programma.

Che dire delle variabili extern ? Hanno ancora lo stesso problema delle variabili globali "reali"? Forse non devo nemmeno fare questa domanda e andare avanti se penso che quello che sto facendo sia giusto, ma oggi ho visto un altro "variabili globali sono BAD" tipo di post, e finalmente sono arrivato qui pensando che forse questo è un diritto posto per questo tipo di domanda. Qual è il tuo pensiero?

Questa domanda non è un duplicato di questo perché questa domanda richiede extern e static variabili non locali mentre l'altra domanda riguarda variabili scope-scope e scope-block extern .

    
posta xiver77 27.08.2015 - 18:43
fonte

4 risposte

14

In un programma C ben progettato, una variabile file-static è simile a un membro statico privato di una classe:

  • È possibile accedere alle funzioni solo in quel file, in modo simile a come una variabile membro statica privata può accedere solo alle funzioni della classe in cui è definita.

  • C'è solo una copia della variabile.

  • La sua durata è la durata del programma.

Una variabile extern sarebbe una vera variabile globale come in qualsiasi lingua che li supporta.

Una variabile non globale di static è non peggiore di una globale; in effetti, sono necessari in alcuni casi.

  • L'accesso è controllato tramite le funzioni che scrivi. Ciò aiuta con l'integrità dei dati, compresi il controllo dei limiti e la sicurezza dei thread. (nota: questo non garantisce garantisce la sicurezza del thread, è semplicemente uno strumento per aiutare lungo la strada)

  • I dati sono incapsulati: solo quel file può accedervi. Questo è il più vicino possibile a C per l'incapsulamento in cui più funzioni possono accedere a una variabile statica.

Le variabili globali sono cattive, non importa quale. Le variabili di file statiche presentano i vantaggi di una variabile statica privata ma nessuno degli svantaggi di una variabile globale.

L'unico problema è diverso da una vera variabile statica privata come in C ++, altri file possono dichiarare una variabile extern che corrisponde alla dichiarazione e non è possibile impedire l'accesso. In altre parole, stai facendo affidamento sul sistema di onore per evitare di trasformarlo in una variabile globale.

    
risposta data 27.08.2015 - 19:50
fonte
5

Lo stato globale, comprese le variabili extern e non const static nello scope o nelle funzioni può spesso essere una soluzione facile a un dato problema, ma ci sono tre problemi:

  1. static rende il codice non testabile , poiché le variabili static tendono a essere dipendenze non sostituibili. O in più parole OOP-y: non stai seguendo il principio di inversione delle dipendenze. Sono arrivato a C e C ++ da linguaggi dinamici come Perl, quindi il mio modello di costo è inclinato verso dispatch virtuali e funzioni e così via. Con i linguaggi attuali, c'è un certo conflitto tra testabilità e buona architettura, ma penso che il minor fastidio nel rendere esplicite le dipendenze e nel lasciarli sovrascrivere nei test sia notevolmente compensato dalla facilità con cui si scrivono i test, assicurando così che il software funzioni come previsto. Senza rendere il tuo codice più dinamico, l'unico meccanismo disponibile per iniettare le dipendenze per un test è la compilazione condizionale.

  2. Lo stato globale rende difficile ragionare sulla correttezza e ciò porta a bug. Più pezzi e pezzi hanno accesso a una variabile e possono modificarla, più è facile perdere traccia di ciò che sta accadendo. Invece: preferisci un singolo incarico di variabili! Preferisci const ovunque ragionevole! Preferisci le variabili di guardia attraverso getter e setter in cui puoi introdurre controlli di correttezza. Finché lo stato è static e non extern , è ancora possibile per mantenere la correttezza, ma è sempre meglio presumere che io-in-una-settimana non sarà intelligente come io adesso. Soprattutto in C ++, possiamo usare le classi per modellare varie astrazioni che rendono impossibile l'uso improprio di qualcosa, quindi cerca di utilizzare il sistema di tipo piuttosto che la tua intelligenza: hai cose più importanti a cui pensare.

  3. Lo stato globale potrebbe implicare che le tue funzioni siano non rientranti o che possano essere utilizzate solo in un contesto alla volta. Immagina un driver per database che possa gestire solo una connessione! Questa è una restrizione totalmente inutile. In realtà, le limitazioni sono spesso più sottili, come una variabile globale utilizzata per aggregare i risultati. Invece, rendere il flusso di dati esplicito e passare tutto attraverso i parametri di funzione. Ancora una volta, le classi C ++ possono renderlo più gestibile.

Ovviamente, static const NAMED_CONSTANTS è OK. Usare static all'interno delle funzioni è molto più complicato: mentre è utile per le costanti inizializzate pigramente, potrebbe essere abbastanza non verificabile. Un compromesso consiste nel separare il calcolo del valore iniziale dalla variabile statica, in modo che entrambe le parti possano essere testate separatamente.

Nei piccoli programmi autonomi, tutto questo non ha importanza e puoi continuare a utilizzare lo stato di static per la gioia del tuo cuore. Ma quando passi intorno a 500 LOC o se stai scrivendo una libreria riutilizzabile, dovresti davvero iniziare a pensare a una buona architettura ea una buona interfaccia senza restrizioni inutili.

    
risposta data 27.08.2015 - 19:50
fonte
1

Non considero le variabili con ambito di file come le variabili globali. Dopo tutto, tutti gli accessi a queste variabili sono confinati in un unico file sorgente. Con questa restrizione, le variabili per l'ambito dei file sono più o meno buone o cattive di un membro di dati statici privati di C ++ e non ne vietate l'uso, vero?

    
risposta data 27.08.2015 - 19:23
fonte
0

È tutto legato alla portata della variabile (non costante, qualcosa di mutabile) secondo me. È una visione che certamente manca di qualche sfumatura, ma è un contatore pragmatico e un appello a tornare ai fondamenti di base di coloro che dicono "Questo è assolutamente malvagio!" solo per poi saltare su questioni simili a quelle associate a ciò che criticano, come le condizioni di gara.

Immagina di avere una funzione di linea di 50.000 con tutti i tipi di variabili dichiarate in cima e goto istruzioni per saltare dappertutto. Non è molto piacevole con un ambito mostruoso così variabile, e tentare di ragionare sulla funzione, e su cosa sta succedendo con tali variabili, sarà estremamente difficile. In un caso così mostruoso, la normale distinzione tra effetto collaterale "esterno" e "interno" perde molto del suo scopo pratico.

Immagina di avere un semplice programma di 80 righe che scrivi una sola volta e usa una variabile globale (sia con linkage interno che con scope di file o linkage esterno, ma in entrambi i casi il programma è teeny). Non è così male.

Immagina di avere una classe mostruosa in un linguaggio orientato agli oggetti che contiene la logica dell'intero programma con migliaia e migliaia di linee di codice per la sua implementazione. In questo caso le sue variabili membro sono più problematiche delle globali del programma sopra, 80 linee.

Se vuoi essere in grado di ragionare in modo migliore e più sicuro sul tuo codice, la sicurezza del thread (o la sua mancanza) lo rende più prevedibile, assicurati che i tuoi test abbiano una buona copertura senza che tutti i tipi di potenziali edge case siano mancato, ecc., quindi aiuta a restringere l'accesso alle variabili.

Le statistiche sull'ambito dei file tenderanno ad avere un ambito più ristretto rispetto a quelli con linkage esterno, ma poi di nuovo se il tuo file sorgente è di 100.000 linee di codice, che è ancora abbastanza dannatamente ampio. Quindi, per le statistiche sull'ambito dei file, se non puoi evitarli, proverei a mantenere il loro ambito ristretto non rendendo il tuo file sorgente che può accedervi ginormous, poiché in tal caso ridurre il suo ambito è ridurre le dimensioni e lo scopo del design del file sorgente, al contrario della funzione (per le variabili locali, inclusi i parametri), della classe (per le variabili membro) o eventualmente del modulo (per le globali con collegamento esterno ma accessibile solo all'interno del modulo), o anche dell'intero software (per i globali con collegamento esterno accessibile a tutto il software).

    
risposta data 20.12.2018 - 22:03
fonte

Leggi altre domande sui tag