webentwicklung-frage-antwort-db.com.de

Wann ist es in Ordnung, eine globale Variable in C zu verwenden?

Anscheinend gibt es eine große Vielfalt an Meinungen, von "Niemals!" Bis "Es ist keine große Sache - benutze sie, wenn es ist bequemer als nicht. "

So.

Spezifische, konkrete Gründe (vorzugsweise mit einem Beispiel)

  • Warum globale Variablen gefährlich sind
  • Bei globalen Variablen sollteanstelle von Alternativen verwendet werden
  • Welche Alternativen gibt es für diejenigen, die versucht sind, globale Variablen unangemessen zu verwenden?

Obwohl dies subjektiv ist, werde ich eine Antwort auswählen (die für mich am besten die Hassliebe darstellt, die jeder Entwickler zu Globals haben sollte) und die Community wird ihre Antwort direkt darunter bewerten.

Ich glaube, es ist wichtig, dass Neulinge diese Art von Referenz haben, aber bitte machen Sie sich keine Sorgen, wenn eine andere Antwort existiert, die Ihrer im Wesentlichen ähnlich ist - fügen Sie einen Kommentar hinzu oder bearbeiten Sie die Antwort einer anderen Person.

-Adam

43
Adam Davis

Variablen sollten immer den kleineren möglichen Bereich haben. Das Argument dahinter ist, dass jedes Mal, wenn Sie den Gültigkeitsbereich erhöhen, mehr Code vorhanden ist, der die Variable möglicherweise ändert, und somit mehr Komplexität in der Lösung entsteht. 

Es ist daher klar, dass die Vermeidung von globalen Variablen bevorzugt wird, wenn das Design und die Implementierung dies natürlich zulassen. Aus diesem Grund möchte ich keine globalen Variablen verwenden, es sei denn, sie werden wirklich benötigt.

Ich kann auch der Aussage "never" nicht zustimmen. Wie jedes andere Konzept sind auch globale Variablen ein Werkzeug, das bei Bedarf verwendet werden sollte. Ich würde eher globale Variablen als künstliche Konstrukte verwenden (z. B. das Weitergeben von Zeigern), die nur die tatsächliche Absicht maskieren. Gute Beispiele, bei denen globale Variablen verwendet werden, sind Implementierungen von Einzelmuster oder Registerzugriff in eingebetteten Systemen.

Wie kann ein übermäßiger Gebrauch von globalen Variablen tatsächlich erkannt werden: Inspektion, Inspektion, Inspektion. Wann immer ich eine globale Variable sehe, muss ich mich fragen: Wird dies wirklich global benötigt?

42

Die einzige Möglichkeit, globale Variablen zum Laufen zu bringen, besteht darin, ihnen Namen zu geben, die sicherstellen, dass sie eindeutig sind.

Dieser Name hat normalerweise ein Präfix, dem einige "Module" oder eine Sammlung von Funktionen zugeordnet sind, für die die globale Variable besonders fokussiert oder sinnvoll ist.

Das bedeutet, dass die Variable zu diesen Funktionen "gehört" - sie ist Teil dieser Funktionen. Tatsächlich kann das Globale normalerweise mit einer kleinen Funktion "umschlossen" werden, die mit den anderen Funktionen einhergeht - in demselben .h-Datei gleichen Namenspräfix.

Bonus.

Wenn Sie das tun, ist es plötzlich nicht mehr wirklich global. Es ist jetzt Teil eines Moduls verwandter Funktionen.

Dies kann immer erfolgen. Mit ein wenig Nachdenken kann jede früher globale Variable einer Sammlung von Funktionen zugewiesen werden, einer bestimmten .h-Datei zugeordnet und mit Funktionen isoliert werden, mit denen Sie die Variable ändern können, ohne irgendetwas zu beschädigen.

Anstatt "niemals globale Variablen verwenden" zu sagen, können Sie sagen "weisen Sie die Verantwortlichkeiten der globalen Variablen einem Modul zu, wo es am sinnvollsten ist." 

15
S.Lott

Betrachten Sie diesen Koan: "Wenn der Umfang eng genug ist, ist alles global".

In diesem Alter ist es immer noch möglich, ein sehr schnelles Hilfsprogramm für eine einmalige Arbeit zu schreiben.

In solchen Fällen ist die Energie, die zum Erstellen eines sicheren Zugriffs auf Variablen erforderlich ist, größer als die Energie, die durch das Debuggen von Problemen in einem so kleinen Dienstprogramm eingespart wird.

Dies ist der einzige Fall, an den ich ohne weiteres denken kann, wo globale Variablen sinnvoll sind, und das ist relativ selten. Nützliche, neuartige Programme, die so klein sind, dass sie vollständig im Kurzzeitgedächtnis des Gehirns gehalten werden können, werden immer seltener, sie existieren jedoch immer noch.

In der Tat könnte ich mutig behaupten, dass globale Variablen illegal sein sollten, wenn das Programm nicht so klein ist.

  • Wenn sich die Variable niemals ändert, ist sie eine Konstante, keine Variable.
  • Wenn für die Variable ein universeller Zugriff erforderlich ist, sollten zwei Unterprogramme vorhanden sein, um sie abzurufen und festzulegen, und sie sollten synchronisiert werden.
  • Wenn das Programm klein beginnt und später möglicherweise größer wird, muss der Code so aussehen, als wäre das Programm heute groß, und globale Variablen werden abgeschafft. Nicht alle Programme werden wachsen! (Das setzt natürlich voraus, dass der Programmierer gelegentlich Code wegwirft.)
11
Paul Brinkley

Globale Variablen in C sind hilfreich, um Code lesbarer zu machen, wenn eine Variable von mehreren Methoden benötigt wird (anstatt die Variable in jede Methode zu übergeben). Sie sind jedoch gefährlich, da alle Standorte die Möglichkeit haben, diese Variable zu ändern, wodurch es möglicherweise schwierig wird, Fehler zu finden. Wenn Sie eine globale Variable verwenden müssen, stellen Sie immer sicher, dass sie nur direkt von einer Methode geändert wird und alle anderen Aufrufer diese Methode verwenden. Dies erleichtert das Debuggen von Problemen, die sich auf Änderungen dieser Variablen beziehen.

8
Jeff Yates

Wenn Sie sich keine Sorgen um Thread-sicheren Code machen : Verwenden Sie sie, wo immer es Sinn macht, mit anderen Worten, wo immer es sinnvoll ist, etwas als globalen Zustand auszudrücken.

Wenn Ihr Code möglicherweise multithreaded ist : um jeden Preis vermeiden. Abstrakte globale Variablen in Arbeitswarteschlangen oder eine andere Thread-sichere Struktur einschließen oder, wenn es unbedingt notwendig ist, sie in Sperren einwickeln, wobei zu berücksichtigen ist, dass dies mögliche Engpässe im Programm sind.

7
Dan Lenski

Ich kam aus dem "Nie" -Lager, bis ich in der Rüstungsindustrie anfing. Es gibt einige Industriestandards, die erfordern, dass Software globale Variablen anstelle von dynamischem Speicher (malloc im Fall C) verwendet. Ich muss meine Herangehensweise an die dynamische Speicherzuweisung für einige der Projekte, an denen ich arbeite, überdenken. Wenn Sie "globalen" Speicher mit den entsprechenden Semaphoren, Threads usw. schützen können, kann dies ein akzeptabler Ansatz für Ihre Speicherverwaltung sein.

4
gthuffman

Die Komplexität des Codes ist nicht die einzige Optimierung des Problems. Für viele Anwendungen hat die Leistungsoptimierung eine weitaus höhere Priorität. Noch wichtiger ist jedoch, dass die Verwendung globaler Variablen die Codekomplexität in vielen Situationen drastisch reduzieren kann. Es gibt viele, möglicherweise spezialisierte Situationen, in denen globale Variablen nicht nur eine akzeptable Lösung sind, sondern bevorzugt werden. Mein bevorzugtes Spezialbeispiel ist ihre Verwendung, um die Kommunikation zwischen dem Haupt-Thread einer Anwendung mit einer Audio-Rückruffunktion, die in einem Echtzeit-Thread ausgeführt wird, bereitzustellen.

Es ist irreführend anzunehmen, dass globale Variablen in Multithread-Anwendungen ein Nachteil sind, da jede Variable ANY unabhängig von ihrem Geltungsbereich eine potenzielle Gefahr darstellt, wenn sie in mehr als einem Thread Änderungen unterworfen ist.

Verwenden Sie globale Variablen sparsam. Datenstrukturen sollten wann immer möglich verwendet werden, um die Verwendung des globalen Namensraums zu organisieren und zu isolieren.

Variabler Umfang bietet Programmierern einen sehr nützlichen Schutz - aber es kann Kosten verursachen. Ich bin heute Abend gekommen, um über globale Variablen zu schreiben, weil ich ein erfahrener Objective-C-Programmierer bin, der oft frustriert ist, wo Hindernisse für den Objektzugriff auf Objektorientierung liegen. Ich würde argumentieren, dass anti-globaler Eiferer vor allem von jüngeren, theoretisch durchdachten Programmierern ausgehen, die hauptsächlich mit objektorientierten APIs isoliert sind, ohne tiefgehende praktische Erfahrung mit APIs auf Systemebene und deren Interaktion in der Anwendungsentwicklung. Aber ich muss zugeben, dass ich frustriert bin, wenn Anbieter den Namespace schlampig nutzen. Bei einigen Linux-Distributionen waren beispielsweise "PI" und "TWOPI" global vordefiniert, wodurch ein großer Teil meines persönlichen Codes gebrochen wurde. 

3
ctpenrose

Sie müssen berücksichtigen, in welchem ​​Kontext auch die globale Variable verwendet wird. In Zukunft soll dieser Code dupliziert werden.

Zum Beispiel, wenn Sie einen Socket innerhalb des Systems verwenden, um auf eine Ressource zuzugreifen. In der Zukunft möchten Sie auf mehr als eine dieser Ressourcen zugreifen. Wenn die Antwort "Ja" lautet, würde ich mich in erster Linie von Globals fernhalten, so dass kein größerer Refaktor erforderlich ist.

2
JProgrammer

Es ist ein Werkzeug wie jedes andere normalerweise überlastet, aber ich denke nicht, dass sie böse sind.

Ich habe zum Beispiel ein Programm, das sich wirklich wie eine Online-Datenbank verhält. Die Daten werden im Speicher abgelegt, können jedoch von anderen Programmen bearbeitet werden. Es gibt interne Routinen, die sich wie gespeicherte Prozeduren und Auslöser in einer Datenbank verhalten.

Dieses Programm hat Hunderte von globalen Variablen, aber wenn Sie darüber nachdenken, was ist eine Datenbank, aber eine riesige Anzahl von globalen Variablen. 

Dieses Programm wird seit über zehn Jahren in vielen Versionen verwendet und es war nie ein Problem, und ich würde es in einer Minute wiederholen.

Ich gebe zu, dass in diesem Fall die globalen Variablen Objekte sind, die über Methoden verfügen, um den Status des Objekts zu ändern. Es ist daher kein Problem, herauszufinden, wer das Objekt während des Debuggens geändert hat, da ich in der Routine immer einen Haltepunkt setzen kann, der den Objektstatus ändert. Oder noch einfacher, ich schalte einfach die integrierte Protokollierung ein, die die Änderungen protokolliert.

1
Kevin Gale
  • Wenn nicht zu verwenden: Globale Variablen sind gefährlich, da der einzige Weg, um herauszufinden, wie sich die globale Variable geändert hat, darin besteht, den gesamten Quellcode in der .c-Datei zu verfolgen, in der sie deklariert sind (oder alle .c-Dateien, wenn es ist auch extern) Wenn Ihr Code fehlerhaft ist, müssen Sie Ihre gesamte Quelldatei (en) durchsuchen, um zu sehen, welche Funktionen sie wann ändern. Es ist ein Albtraum zu debuggen, wenn es schief geht. Wir nehmen oft den Einfallsreichtum hinter dem Konzept der lokalen Variablen als selbstverständlich an - es ist leicht zu finden
  • Wann zu verwenden: Globale Variablen sollten verwendet werden, wenn ihre Verwendung nicht übermäßig maskiert ist und die Kosten für die Verwendung lokaler Variablen so hoch sind, dass sie die Lesbarkeit beeinträchtigen. Damit meine ich die Notwendigkeit, einen zusätzlichen Parameter hinzufügen zu müssen, um Funktionsargumente und Rückgabewerte zu übergeben und Zeiger zu übergeben. Drei klassische Beispiele: Wenn ich den Pop-Stack und den Push-Stack verwende, wird dies von Funktionen gemeinsam genutzt. Natürlich könnte ich lokale Variablen verwenden, aber dann müsste ich Zeiger als zusätzlichen Parameter herumgeben. Ein zweites klassisches Beispiel ist in K & Rs "The C Programming Language" zu finden, wo sie eine getch () und ungetch () - Funktion definieren, die ein globales Zeichenpufferarray gemeinsam nutzen. Wieder einmal müssen wir es nicht global machen, aber lohnt sich die zusätzliche Komplexität, wenn es ziemlich schwierig ist, die Verwendung des Puffers durcheinander zu bringen? Das dritte Beispiel ist etwas, das Sie im eingebetteten Raum unter Hobbyisten von Arduino finden. Viele Funktionen innerhalb der main loop - Funktion haben alle die millis () - Funktion gemeinsam. Dies ist die momentane Zeit, zu der die Funktion aufgerufen wird. Da die Taktrate nicht unendlich ist, unterscheiden sich millis () will innerhalb einer einzigen Schleife . Um dies konstant zu machen, machen Sie zu jeder Schleife eine Momentaufnahme der Zeit prior und speichern Sie sie in einer globalen Variablen. Der Zeit-Snapshot ist jetzt derselbe wie bei Zugriff durch die vielen Funktionen.
  • Alternativen: Nicht viel. Halten Sie sich so weit wie möglich an das lokale Scoping, besonders zu Beginn des Projekts, und nicht umgekehrt. Wenn das Projekt wächst, und wenn Sie der Meinung sind, dass die Komplexität durch globale Variablen herabgesetzt werden kann, tun Sie dies, aber nur, wenn es die Anforderungen von Punkt zwei erfüllt. Denken Sie daran, dass die Verwendung des lokalen Bereichs und komplizierterer Code das geringere Übel ist, verglichen mit dem unverantwortlichen Verwenden globaler Variablen.
1
Dean P

Globale Variablen sollten verwendet werden, wenn mehrere Funktionen auf die Daten zugreifen oder auf ein Objekt schreiben müssen. Wenn Sie beispielsweise Daten oder eine Referenz an mehrere Funktionen übergeben mussten, z. B. eine einzelne Protokolldatei, einen Verbindungspool oder eine Hardware-Referenz, auf die anwendungsübergreifend zugegriffen werden muss. Dies verhindert sehr lange Funktionsdeklarationen und große Zuordnungen von duplizierten Daten. 

Sie sollten in der Regel nicht globale Variablen verwenden, es sei denn, dies ist absolut notwendig, da globale Variablen nur bereinigt werden, wenn Sie ausdrücklich dazu aufgefordert werden oder Ihr Programm beendet wird. Wenn Sie eine Multithread-Anwendung ausführen, können mehrere Funktionen gleichzeitig in die Variable schreiben. Wenn Sie einen Fehler haben, kann das Nachverfolgen dieses Fehlers schwieriger sein, da Sie nicht wissen, welche Funktion die Variable ändert. Sie stoßen auch auf das Problem der Namenskonflikte, es sei denn, Sie verwenden eine Namenskonvention, die explizit globalen Variablen einen eindeutigen Namen gibt.

1
Steropes

Wenn Sie Konstanten deklarieren.

1
user15039

Mir fallen mehrere Gründe ein:

debugging/Testzwecke (Warnung - Diesen Code nicht getestet):

#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();
};

Ich habe Bereichsvariablen für fib2 verwendet, aber dies ist ein weiteres Szenario, in dem Globals nützlich sein könnten (reine mathematische Funktionen, die Daten speichern müssen, um eine Ewigkeit zu vermeiden).

programme, die nur einmal verwendet werden (z. B. für einen Wettbewerb) oder wenn die Entwicklungszeit verkürzt werden muss

globals sind als typisierte Konstanten nützlich, bei denen eine Funktion irgendwo * int anstelle von int benötigt.

Ich vermeide generell Globals, wenn ich das Programm länger als einen Tag nutzen möchte.

1
yingted

Ich bin hier im "niemals" -Lager. Wenn Sie eine globale Variable benötigen, verwenden Sie mindestens ein Singleton-Muster . Auf diese Weise profitieren Sie von Lazy Instantiation und stören den globalen Namespace nicht.

0
Nik Reiman

Globale Konstanten sind nützlich - Sie erhalten mehr Typsicherheit als Vorverarbeitungs-Makros, und es ist genauso einfach, den Wert zu ändern, wenn Sie möchten.

Globale Variablen haben einige Verwendungszwecke, beispielsweise wenn der Betrieb vieler Teile eines Programms von einem bestimmten Zustand in der Zustandsmaschine abhängt. Solange Sie die Anzahl der Stellen einschränken, die die Variable verändern können, ist es nicht allzu schlimm.

Globale Variablen werden fast gefährlich, sobald Sie mehrere Threads erstellen. In diesem Fall sollten Sie den Gültigkeitsbereich auf (höchstens) eine globale Datei (durch Angabe statischer Variablen) und auf Getter/Setter-Methoden beschränken, die sie vor mehrfachem Zugriff schützen, wo dies gefährlich sein könnte.

0
Adam Hawes