it-swarm.com.de

Sind statische Variablen im Dateibereich in C genauso schlecht wie globale globale Variablen?

In C verwenden Sie häufig/manchmal (aus Stilgründen) eine Variable für den Dateibereich static, in der Sie eine private Klassenmitgliedsvariable in C++ verwenden. Wenn Sie auf Multithread-Programme skalieren, fügen Sie einfach thread_local in C11 oder der lang unterstützten Erweiterung __thread passt gut. Ich weiß, dass Sie in C genau das Gleiche tun können wie in C++, indem Sie alles in ein struct einfügen und eine Reihe von Funktionen erstellen, die als erstes Argument einen Zeiger auf dieses struct verwenden. Einige Bibliotheken tun dies ausgiebig. Aber mein persönlicher Stil ist es, ein struct bei Bedarf so klein wie möglich zu halten.

Ich lese oder höre oft Leute, die argumentieren, dass 'globale' Variablen so schlecht sind. Ich folge ihren Gründen, und die meisten ihrer Argumente scheinen sich auf extern globale Variablen in C-Begriffen zu beziehen. Was sie sagen, ist sicherlich wahr. Ich verwende manchmal 1 oder 2 von extern deklarierten Variablen im gesamten Programm, wenn dies die Dinge erheblich vereinfacht und wenn es einfach ist, sie im Auge zu behalten, aber wenn Sie weiter gehen, wird ein Programm leicht unvorhersehbar.

Was ist mit static Variablen? Haben sie immer noch das gleiche Problem wie "echte" globale Variablen? Vielleicht muss ich diese Frage nicht einmal stellen und weitermachen, wenn ich denke, dass das, was ich tue, richtig ist, aber heute habe ich einen anderen Beitrag vom Typ "Globale Variablen sind SCHLECHT" gesehen und bin schließlich hierher gekommen und habe gedacht, dass dies vielleicht richtig ist Platz für solche Fragen. Was ist dein Gedanke?

Diese Frage ist kein Duplikat von this , da in dieser Frage nach nicht lokalen Variablen extern und static gefragt wird, während sich die andere Frage auf Dateibereich und Blockbereich bezieht static Variablen.

8
xiver77

In einem gut gestalteten C-Programm ähnelt eine dateistatische Variable einem privaten statischen Mitglied einer Klasse:

  • Auf sie kann nur von Funktionen in dieser Datei zugegriffen werden, ähnlich wie auf eine private statische Mitgliedsvariable nur von Funktionen in der Klasse zugegriffen werden kann, in der sie definiert ist.

  • Es gibt nur eine Kopie der Variablen.

  • Seine Lebensdauer ist die Programmlebensdauer.

Eine extern -Variable wäre eine echte globale Variable wie in jeder Sprache, die sie unterstützt.

Eine nicht globale Variable static ist nicht so schlecht wie eine globale; in der Tat sind sie in einigen Fällen notwendig.

  • Der Zugriff wird über die von Ihnen geschriebenen Funktionen gesteuert. Dies hilft bei der Datenintegrität, einschließlich der Überprüfung der Grenzen sowie der Thread-Sicherheit. (Hinweis: Dies ist nicht Garantie Thread-Sicherheit, es ist einfach ein Werkzeug, um auf dem Weg zu helfen)

  • Daten sind gekapselt: Nur diese Datei kann darauf zugreifen. Dies ist so nah wie C an der Kapselung, bei der mehrere Funktionen auf eine statische Variable zugreifen können.

Globale Variablen sind auf jeden Fall schlecht. Statische Dateivariablen haben die Vorteile einer privaten statischen Variablen, aber keinen der Nachteile einer globalen Variablen.

Das einzige Problem ist, dass andere Dateien im Gegensatz zu einer echten privaten statischen Variablen wie in C++ eine extern -Variable deklarieren können, die der Deklaration entspricht, und Sie den Zugriff nicht verhindern können. Mit anderen Worten, Sie verlassen sich auf das Ehrensystem, um zu vermeiden, dass es zu einer globalen Variablen wird.

17
user22815

Der globale Status, einschließlich extern Variablen und Nicht -conststatic Variablen im Dateibereich oder in Funktionen, kann häufig eine einfache Lösung für ein bestimmtes Problem sein, es gibt jedoch drei Probleme:

  1. static macht Code nicht testbar, weil static Variablen in der Regel nicht ersetzbare Abhängigkeiten sind. Oder in mehr OOP-y-Worten: Sie folgen nicht dem Prinzip der Abhängigkeitsinversion. Ich bin aus dynamischen Sprachen wie Perl zu C und C++ gekommen, daher ist mein Kostenmodell auf virtuelle Versand- und Funktionszeiger usw. ausgerichtet. Bei den aktuellen Sprachen gibt es einen Konflikt zwischen Testbarkeit und guter Architektur, aber ich denke, dass das kleine Ärgernis, Ihre Abhängigkeiten explizit zu machen und sie in Tests überschreiben zu lassen, durch die einfache Schreibbarkeit von Tests spürbar ausgeglichen wird und somit sicherstellt, dass Ihre Software funktioniert erwartet. Ohne Ihren Code dynamischer zu gestalten, ist der einzige verfügbare Mechanismus zum Einfügen von Abhängigkeiten für einen Test die bedingte Kompilierung.

  2. Der globale Zustand macht es schwer über Korrektheit zu begründen, und das führt zu Fehlern. Je mehr Teile auf eine Variable zugreifen und diese ändern können, desto einfacher ist es, den Überblick über das Geschehen zu verlieren. Stattdessen: einzelne Zuordnung von Variablen bevorzugen! Bevorzugen Sie const wo immer es vernünftig ist! Ziehen Sie es vor, Variablen durch Getter und Setter zu schützen, in denen Sie Korrektheitsprüfungen einführen können. Solange der Status static und nicht extern ist, ist es immer noch möglich , die Korrektheit aufrechtzuerhalten, aber es ist immer so Es ist besser anzunehmen, dass ich in einer Woche nicht so schlau bin wie ich. Insbesondere in C++ können wir Klassen verwenden, um verschiedene Abstraktionen zu modellieren, die es unmöglich machen, etwas zu missbrauchen. Versuchen Sie also, das Typsystem anstelle Ihrer Intelligenz zu verwenden - Sie müssen über wichtigere Dinge nachdenken.

  3. Der globale Status kann bedeuten, dass Ihre Funktionen nicht wiedereintrittsfähig sind oder dass sie jeweils nur in einem Kontext verwendet werden können. Stellen Sie sich einen Datenbanktreiber vor, der nur eine Verbindung verwalten kann! Das ist eine völlig unnötige Einschränkung. In der Realität sind die Einschränkungen häufig subtiler, z. B. eine globale Variable, mit der Ergebnisse aggregiert werden. Machen Sie stattdessen Ihren Datenfluss explizit und übergeben Sie alles über Funktionsparameter. Auch hier können C++ - Klassen dies leichter handhaben.

Offensichtlich static const NAMED_CONSTANTS Ist ok. Die Verwendung von static innerhalb von Funktionen ist viel schwieriger: Während es für träge initialisierte Konstanten nützlich ist, kann es ziemlich unprüfbar sein. Ein Kompromiss besteht darin, die Berechnung des Anfangswertes von der statischen Variablen zu trennen, damit beide Teile separat getestet werden können.

In kleinen, in sich geschlossenen Programmen spielt dies alles keine Rolle, und Sie können den Status static nach Herzenslust weiter verwenden. Wenn Sie jedoch ungefähr 500 LOC überschreiten oder eine wiederverwendbare Bibliothek schreiben, sollten Sie wirklich über eine gute Architektur und eine gute Benutzeroberfläche ohne unnötige Einschränkungen nachdenken.

6
amon

Ich halte Variablen mit Dateibereich nicht für so schlecht wie globale Variablen. Schließlich sind alle Zugriffe auf diese Variablen auf eine einzige Quelldatei beschränkt. Mit dieser Einschränkung sind Dateibereichsvariablen so gut oder schlecht wie ein privates statisches C++ - Datenelement, und Sie verbieten ihre Verwendung nicht, oder?

Meiner Ansicht nach hängt alles vom Umfang der Variablen ab (nicht konstant, etwas Veränderliches). Es ist eine Ansicht, die zwar keine Nuancen aufweist, aber es ist ein pragmatischer Gegenpol und ein Appell, auf die grundlegendsten Grundlagen für diejenigen zurückzuarbeiten, die sagen: "Das ist absolut böse!" nur um dann auf Themen zu stoßen, die denen ähneln, die mit dem verbunden sind, was sie kritisieren, wie z. B. die Rennbedingungen.

Stellen Sie sich vor, Sie haben eine 50.000-Zeilen-Funktion mit allen Arten von Variablen, die oben deklariert sind, und goto -Anweisungen, um überall herumzuhüpfen. Das ist bei einem solch monströsen Variablenumfang nicht sehr angenehm, und es wird äußerst schwierig sein, über die Funktion und die Vorgänge mit solchen Variablen nachzudenken. In solch einem monströsen Fall verliert die normale Unterscheidung zwischen "äußerer" und "innerer" Nebenwirkung viel von ihrem praktischen Zweck.

Stellen Sie sich vor, Sie haben ein einfaches Programm mit 80 Zeilen, das Sie nur einmal schreiben und mit einer globalen Variablen starten (entweder mit interner Verknüpfung und Dateibereich oder externer Verknüpfung, aber so oder so ist das Programm winzig). Das ist nicht so schlecht.

Stellen Sie sich vor, Sie haben eine monströse Klasse in einer objektorientierten Sprache, die die Logik Ihres gesamten Programms mit Tausenden und Abertausenden von Codezeilen für deren Implementierung enthält. In diesem Fall sind die Mitgliedsvariablen problematischer als die globalen Variablen im obigen Programm mit 80 Zeilen.

Wenn Sie in der Lage sein möchten, Ihren Code besser und sicherer zu beurteilen, die Thread-Sicherheit (oder das Fehlen davon) besser vorhersehbar zu machen, sicherzustellen, dass Ihre Tests eine gute Abdeckung aufweisen, ohne dass alle Arten von potenziellen Edge-Fällen übersehen werden usw. Dann hilft es, den Zugriff auf Variablen einzugrenzen.

Die Statik des Dateibereichs hat tendenziell einen engeren Bereich als die mit externer Verknüpfung. Wenn Ihre Quelldatei jedoch 100.000 Codezeilen umfasst, ist sie immer noch verdammt breit. Wenn Sie sie für die Statik des Dateibereichs nicht vermeiden können, würde ich versuchen, ihren Gültigkeitsbereich eng zu halten, indem Sie Ihre Quelldatei, die auf sie zugreifen kann, nicht riesig machen, da in diesem Fall die Reduzierung des Gültigkeitsbereichs eine Reduzierung der Größe und des Entwurfsbereichs bedeutet der Quelldatei im Gegensatz zur Funktion (für lokale Variablen, einschließlich Parameter), Klasse (für Mitgliedsvariablen) oder möglicherweise Modul (für Globals mit externer Verknüpfung, aber nur innerhalb des Moduls zugänglich) oder sogar der gesamten Software (für Globals mit externe Verknüpfung für die gesamte Software zugänglich).

1
Dragon Energy