Globale Variable in C
-
SaHel schrieb:
Als Beispiel sei etwa das C GUI GTK+ angeführt. Dort gibt es eine Möglichkeit User-data an die verschiedenen Callbackfunktionen zu übergeben. Ist es nicht sinnvoller solche Daten dazu gehören ja nicht nur die reinen technischen Felder, sondern etwa auch applikatorische Felder z.B. aus einer DB.
Was ist nicht sinnvoller? Was??
Bitte ein wenig konkreter machen, sonst wird das eine Wischiwaschi-Diskussion mit abstrakten Argumenten wie "lokal ist der Zugriff tendenziell schneller" oder "weniger global ist übersichtlicher".
-
Interessant eure Meinung...
Wenn ich das richtig verstanden habe, begünstigen globale Variable sagen wir "Flüchtigkeitsfehler"??
Das darf IMO kein Argument sein. Wenn meine Variable, wie auch immer, wenn ich sie benutze (zum ersten mal im Programm oder auch später im Programmablauf nochmals, sollte sie doch immer in einem genau definierten Zustand sein...). Ich will niemand zu Nahe treten: Wenn das nicht so ist - dann programmiere ich doch Murks.
Ich habe lange Zeit Programmierersupport (Cobol) gemacht. Nehmen wir an, es kommt einer mit einem 0C7 Exception (dec. gepackte Zahl ist ungültig). Also suchen wir gemeinsam das Feld, lokalisieren es - nun gut. Einen Tag später sagt dieser Programmierer, er habe das Problem gelöst indem er einen "initialize working-storage" eingebaut hat. (Sämtliche Variable werden dadurch auf eine gültige Grundstellung gebracht). Einem solchen Programmierer sollte man sein Prog um die Ohren schmeissen, denn er hat den Fehler nicht behoben. Warum spricht der eine Variable mit ungültigem Wert an? Ist das vielleicht ein DB Problem? Ist das Design vom Prog falsch? usw...
So ähnlich kommt mir das mit globalen Variablen vor....
@Belli Sauber strukturierte Working Felder die richtig prefixiert sind lassen so ein Problem eher nicht aufkommen. Ich finde es eher schrecklich wenn alle Schleifenzähler i oder j heissen. Wobei bei temp. Working Variablen innerhalb eine Subroutine sehe ich durchaus den Sinn...
SaHel
-
Naja i und j finde ich persönlich besser als schleifen_index ^^
Globale Variablen haben halt vor allem das Problem dass man nie so genau weiß wo sie überall bearbeitet werden während lokale Variablen nur auf einen übersichtlichen Teil des Codes beschränkt sind
Natürlich sind globale Variablen trotzdem manchmal sinnvoll nur sollte man lokale Variablen globalen bevorzugen
-
SaHel schrieb:
Das darf IMO kein Argument sein. Wenn meine Variable, wie auch immer, wenn ich sie benutze (zum ersten mal im Programm oder auch später im Programmablauf nochmals, sollte sie doch immer in einem genau definierten Zustand sein...). Ich will niemand zu Nahe treten: Wenn das nicht so ist - dann programmiere ich doch Murks.
Also falls du mich und mein Beispiel damit meinst: ich habe es schon erklärt. Stell dir vor, dein Programm läuft 2 mal parallel. Und eine Änderung der Globalen im ersten Programm bewirkt plötzlich, dass der gleiche Wert im zweiten Programm läuft. Das ist einfach eine Eigenschaft von Windows und ocx-Dateien, die man kennen muss, um nicht in diese Falle zu tappen. Ich habe keinen Murks programmiert, sondern einfach diese (aber auch nur diese) Variable in der Form genutzt, die du hier so verteidigst (als Globale). Dieser Problemfall wird ja auch in meinem Link erklärt:
Memory allocation issues -- Some environments have memory allocation schemes that make allocation of globals tricky. This is especially true in languages where "constructors" have side-effects other than allocation (because, in that case, you can express unsafe situations where two globals mutually depend on one another). Also, when dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.
Hast du dir die Links durchgelesen? Da stehen viele nachvollziehbare Gründe gegen globale Variablen drin. Das heißt nicht, dass im Einzelfall auch mal eine Globale ok sein kann. Aber die Tendenz sollte ganz stark dahin gehen, dass man sie vermeidet.
-
SaHel schrieb:
Interessant eure Meinung...
Wenn ich das richtig verstanden habe, begünstigen globale Variable sagen wir "Flüchtigkeitsfehler"??
Begünstigen ist das falsche Wort, sag lieber "provozieren".
Das darf IMO kein Argument sein. Wenn meine Variable, wie auch immer, wenn ich sie benutze (zum ersten mal im Programm oder auch später im Programmablauf nochmals, sollte sie doch immer in einem genau definierten Zustand sein...). Ich will niemand zu Nahe treten: Wenn das nicht so ist - dann programmiere ich doch Murks.
Hast du tatsächlich schonmal mit mehreren Programmierern an einem großen Projekt gearbeitet? Der obige Absatz klingt so, als hättest du das nicht, denn diese Einstellung ist naiv. Der nächst Absatz klingt aber so, als hättest du es doch schon einmal gemacht:
Ich habe lange Zeit Programmierersupport (Cobol) gemacht. Nehmen wir an, es kommt einer mit einem 0C7 Exception (dec. gepackte Zahl ist ungültig). Also suchen wir gemeinsam das Feld, lokalisieren es - nun gut. Einen Tag später sagt dieser Programmierer, er habe das Problem gelöst indem er einen "initialize working-storage" eingebaut hat. (Sämtliche Variable werden dadurch auf eine gültige Grundstellung gebracht). Einem solchen Programmierer sollte man sein Prog um die Ohren schmeissen, denn er hat den Fehler nicht behoben. Warum spricht der eine Variable mit ungültigem Wert an? Ist das vielleicht ein DB Problem? Ist das Design vom Prog falsch? usw...
Bei globalen Variablen muss der Programmierer den gesamten Quellcode nach allen Vorkommen dieser Variablen zu durchsuchen und bei jedem Vorkommen zu durchdenken, in welchem Zustand das Programm vorher ist, was dort geschieht, und wie der Zustand nachher ist. Und jetzt stell dir mal vor, dass alle Variablen lokal sind und er nur die lokale Funktion anschauen und durchdenken muss. Und jetzt schätz mal ab, um wie vieles produktiver er dann wäre. Und Produktivität ist Geld.
-
linux_c89 schrieb:
Naja i und j finde ich persönlich besser als schleifen_index ^^
Ich halte es so:
Bei Schleifen (vor allem verschachtelten), die mehr als ein, zwei Zeilen Inhalt haben und bei denen mit den Zählvariablen viel gemacht wird, sollten es halbwegs sprechende Namen sein. Wenn ich beispielsweise Bilddaten verarbeite, sagen mir x und y schon mal, dass wohl Zeilen- und Spaltenindizes gemeint sind:
for(int y=0;y<image.height;++y) { for(int x=0;x<image.width;++x) { //... } }
Bei anderen Variablen-Anwendungen, bei denen x und y nicht ausreichen, um den Zweck zu beschreiben, wird dann auch deutlich mehr als ein Zeichen für die Bezeichner verwendet.
Aber hier hat man doch mit Globalen schon verloren, wenn man 20 Funktionen in der ÜE hat, die über die Bilddaten 'rüber gehen. Soll ich mir da jedesmal einen anderen xIrgendwas-Namen ausdenken? Und das, obwohl eine lokale (sogar schleifenlokale!) Variable es auch getan hätte, die dann auch nur dort existiert, wo sie gebraucht wird? Nee, danke.
Und bei simplen Zählschleifen mit vielleicht einer Zeile Inhalt und ohne Verwendung der Zählvariablen im Rumpf reichen i,j usw. völlig aus und verschlechtern auch sicher nicht die Lesbarkeit des Codes.
for(int i=0;i<myMaxSomething;++i) { //mache irgendwas Einzeiliges ohne i... wer braucht da schon einen sprechenden Bezeichner? alles, was man wissen muss, sieht man im Schleifenkopf an der Abbruchbedingung- }
-
Die Wahrheit liegt wahrscheinlich irgendwo in der Mitte.
Sicherlich machen Schleifenzähler, Schalter usw lokal richtig Sinn aber
je nach Toolkit für grafische Oberflächen machen aber auch globale
Variablen Sinn und ersparen das am Anfang genannte massenweise
Übergeben von parametern, Strukturen usw. Wenn z. B die Eingabefelder einer
Maske dann auch noch schön sprechend benannt sind (Pferdename, Pferderasse..),
dann spricht für mich - und ich entwickle seit 1982, nix dagegen, globale
Variablen zu verwenden, sei es nun C, Abap, oder was auch immer.
Ob ich jetzt die Rasse per Knopfdruck prüfe oder per anderem Knopf prüfe,
obs das Pferd schon gibt - alles wurscht - ich hab in jeder Funktion die
Daten direkt ohne 20 Parameter (oder 1ner aber dafür ne Struktur mit 20
Feldern) im Zugriff.
Solls doch jeder machen, wie es für ihn am einfachsten ist.
-
pferdefreund schrieb:
Solls doch jeder machen, wie es für ihn am einfachsten ist.
Klar. Das Problem besteht dann nur für den nächsten armen Trottel, der den Code beackern muss.
-
pferdefreund schrieb:
Die Wahrheit liegt wahrscheinlich irgendwo in der Mitte.
Das ist einer der dümmsten Fehlschlüsse der Menschheitsgeschichte. 1+1 ist 2! Nein 3! Die Wahrheit wird wohl irgendwo in der Mitte liegen...
P.S.: @pferdefreund: Deine Beiträge wären übrigens weitaus besser zu lesen, wenn du nicht selber versuchen würdest, Zeilenumbrüche einzufügen. Nicht jeder verwendet deine Bildschirmauflösung und Zeilenumbrüche setzt der Browser ohnehin automatisch passend.
-
Mal umgekehrt gefragt: warum sollten die Laufvariablen solcher Schleifen global sein? Ist es nicht sinnvoll, Variablen möglichst dort zu deklarieren, wo man sie auch verwendet?
Das einzige Argument, das ich bis jetzt gegen lokale Variablen gelesen haben, war dass es in Cobol anders ist. Nun ja, ähm, es muss ja jemand sagen:
The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense.
-
Wenn ich beispielsweise Bilddaten verarbeite, sagen mir x und y schon mal, dass wohl Zeilen- und Spaltenindizes gemeint sind
Ich denke i,j,k haben sich soweit durchgesetzt das sie sehr aussagekräftig sind
Wenn ich i sehe weiß ich "ah das ist die Zählvariable der äußersten Schleife"
Wenn man mehr als 3 verschachtelungsebenen hat sollte man seinen Code sowieso redesignen (Teile in Funktionen auslagern)
Und wenn die Schleife so lang ist dass die Zählervariable so sprechend sein muss dito^^wenn ich in der Schleife x und y lese würde ich eher an lokale Variablen der Funktion denken, nicht der Schleife
-
linux_c89 schrieb:
Wenn ich beispielsweise Bilddaten verarbeite, sagen mir x und y schon mal, dass wohl Zeilen- und Spaltenindizes gemeint sind
Ich denke i,j,k haben sich soweit durchgesetzt das sie sehr aussagekräftig sind
Wenn ich i sehe weiß ich "ah das ist die Zählvariable der äußersten Schleife"Na ja, ich kann aber y viel leichter mit dem Spaltenindex assoziieren als i. Versteh mich nicht falsch, ich spreche nur von diesem Beispiel (Bilddaten). Ansonsten nehme ich auch i,j,k oder gleich was Deutlicheres. Möglich wären ja noch etwas sprechendere Bezeichner wie z.B. row und col...
-
Danke für euere Antworten.
Zunächst etwas zur Klärung:
Ich verstehe unter "globalen Variablen" Variable, die im Gültigkeitsbereich eines Moduls sind. Linke ich weitere Module hinzu übergebe ich an diese einen klar definierten Storagebereich (analog einem Funktionsaufruf in C), Dieses dazugelinkte Modul benutzt dann den selben klar definierten Speicher wie der Caller. Ich gehe ebenfalls davon aus, dass der selbe Prozess mehrmals aufgerufen einen klar definierten eigenen Storage haben. In der IBM Mainframe Welt - und nur da kann ich mitreden - gibt es für solche mehrfach laufenden Prozesse resp Programme (der Code eines solchen Programmes ist dann nur einmal im Speicher und der Datenbereich wird entsprechend gehandhabt, so dass analog dem BS der selbe Code mehrfach genutzt werden kann) spezielle Programmiertechniken. Stichwort reentrant und reusability.Wenn ich aber solche spezielle Anforderungen nicht habe - etwa bei einer lokalen Desktopapplikation mit gemeinsam genutzter DB uber ein Netz - kann ich mir doch solche Dinge Sparen wie
@pferdefreund schrieb:Sicherlich machen Schleifenzähler, Schalter usw lokal richtig Sinn aber je nach Toolkit für grafische Oberflächen machen aber auch globale Variablen Sinn und ersparen das am Anfang genannte massenweise Übergeben von parametern, Strukturen usw. Wenn z. B die Eingabefelder einer Maske dann auch noch schön sprechend benannt sind (Pferdename, Pferderasse..), dann spricht für mich - und ich entwickle seit 1982, nix dagegen, globale Variablen zu verwenden, sei es nun C, Abap, oder was auch imme.
Das ist genau genommen die Aussage auf die ich hinauswollte und die Intention meines Eröffnungsposts.
@SaHel schrieb:Also: was macht es für einen Sinn, auf globale Variable (ausser etwa technische Schleifen- oder sonst temp. -Felder) möglichst zu verzichten?
Memory allocation issues -- Some environments have memory allocation schemes that make allocation of globals tricky. This is especially true in languages where "constructors" have side-effects other than allocation (because, in that case, you can express unsafe situations where two globals mutually depend on one another). Also, when dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.
Wenn das in manchen Umgebungen so ist (welche?), wäre das natürlich eine Antwort.
Meine obige Klärung von "globalen Variablen" erläutert auch den Einwand von @mngbd
Bitte ein wenig konkreter machen, sonst wird das eine Wischiwaschi-Diskussion mit abstrakten Argumenten wie "lokal ist der Zugriff tendenziell schneller" oder "weniger global ist übersichtlicher".
Und nocheinmal es ging um eine Frage und nicht um einen Religionskrieg. CICS und IMS sind nun mal nicht Windows oder Linux.
-
SaHel schrieb:
Ich verstehe unter "globalen Variablen" Variable, die im Gültigkeitsbereich eines Moduls sind.
Schon der erste Irrtum. Mittels "extern" kann jedes andere Modul auf deine "modulglobalen" Variablen zugreifen, auch schreibend, d.h. sie sind programmglobal. Deshalb macht man sie mittels <static> zu wirklich modulglobal.
-
Wutz schrieb:
Schon der erste Irrtum. Mittels "extern" kann jedes andere Modul auf deine "modulglobalen" Variablen zugreifen, auch schreibend, d.h. sie sind programmglobal. Deshalb macht man sie mittels <static> zu wirklich modulglobal.
extern?? automatisch?? Ich verstehe langsam das C (oder muss ich sagen Unix, Linux, Windows??) Prinzip
Gefunden unter http://wiki.answers.com/Q/What_is_the_use_of_extern_in_CThe "extern" declaration in C is to indicate the existence of, and the type of, a global variable or function. A global variable, or a global function, is one that is available to all C modules (a single C module is typically a single .c file). An extern is something that is defined externally to the current module. In many cases, you can leave off the extern qualifier and not notice any difference because the linker can collapse multiple definitions to one. But the intent is then unclear in the code, and the code is error prone in case of typos. It is much clearer to define the global in one place, and then declare extern references to it in all the other places. When refering to globals provided by a library, especially a shared library, this is even more important in order to ensure you are talking about the correct, common instance of the variable.
Aber so etwas ist ja schrecklich... historisch? konzeptionell?? oder gibt es da einen besonderen Grund? Was für Vorteile hat das?
Ich meine diese Aussage:In many cases, you can leave off the extern qualifier and not notice any difference because the linker can collapse multiple definitions to one. But the intent is then unclear in the code, and the code is error prone in case of typos
-
SaHel schrieb:
Aber so etwas ist ja schrecklich... historisch? konzeptionell?? oder gibt es da einen besonderen Grund? Was für Vorteile hat das?
Ich meine diese Aussage:
...Manchmal teilt man die Typ-Vereinbarungen auf in Definitionen, die Speicherplatz bereitstellen, und Deklarationen, die nur den Typ eines Namens vereinbaren. In diesem Sinn ist die Aussage falsch, weil mehrere gleichzeitig sichtbare Definitionen des gleichen Namens ein Fehler sind. Aber viele Deklarationen, z.B. von Funktionen, erkennt der Compiler auch ohne das 'extern', und wenn er die Definition nicht im betreffenden Modul findet, muss sie eben woanders sein. In solchen Fällen darf man das 'extern' weglassen.
Zum Beispiel zwei Dateien:
#include <stdio.h> /* Definition des Zeigers mit Ziel */ char *global = "global"; /* Definition der Funktion */ void function(void) { puts(global); }
#include <stdio.h> /* Deklaration des Zeigers */ extern char *global; /* Deklaration der Funktion, 'extern' kann man weglassen */ extern void function(void); /* Definition von main() */ int main(void) { puts(global); global = "changed"; function(); return 0; }
-
mngbd schrieb:
Manchmal teilt man die Typ-Vereinbarungen auf in Definitionen, die Speicherplatz bereitstellen, und Deklarationen, die nur den Typ eines Namens vereinbaren. In diesem Sinn ist die Aussage falsch, weil mehrere gleichzeitig sichtbare Definitionen des gleichen Namens ein Fehler sind. Aber viele Deklarationen, z.B. von Funktionen, erkennt der Compiler auch ohne das 'extern', und wenn er die Definition nicht im betreffenden Modul findet, muss sie eben woanders sein. In solchen Fällen darf man das 'extern' weglassen.
Wenn ich das jetzt richtig verstanden habe, besteht bei diesen globalen Variablen die Gefahr, dass sie vom Compiler als extern interpretiert werden können - etwa bei Namensgleichheit und fehlender Dekleration in einem anderen Modul.
In der von mir eingangs beschriebenen "EDV Welt" gibt es so etwas so explizit nicht. Dort werden zwischen verschiedenen Modulen lediglich Anfangsadressen übergeben - und zwar vom Caller (ähnlich eines Funktionsaufrufs in C innerhalb eines Moduls). Damit der Called mit dieser Adresse etwas anfangen kann arbeitet man dort mit sogenannten "Copybooks". Das sieht dann stark vereinfacht etwa so aus:
Prog1 [...] copy "kdstamm". (Felder von kdstamm werden beim Compile hier eingefügt) kdnr int kdname char[xx] (aber anderer Syntax als in C) [...] Und im Code dann: CALL "PROG2" USING kdstamm. (Adresse von kdstamm wird übergeben) [...]
In Prog2 wird dann dem Compiler gesagt, dass es sich nicht um eigenen Storage handelt:
Prog2 [...] Spezieller Linkbereich copy "kdstamm". (Felder von kdstamm werden beim Compile hier ebenfalls eingefügt) kdnr int kdname char[xx] [...] Un beim Start (Code) wird dem Compiler dann via USING kdstamm mitgeteilt, dass bei diesem kdstamm die übergebene Adresse vom Caller stammt (*kdstamm aus Prog1). Die Namensgebung ist dabei egal (analog C). [...] Macht irgendwas mit kdnr, kdname.... [...]
Mit dieser Technik verwaltet immer der Caller den gemeinsamen Speicher für den Called. Und die ganze Sache ist transparent.
Nun ja - andere Welten andere Sitten
Besten Dank für eure Antworten, die Frage ist damit beantwortet....
-
SaHel schrieb:
Wenn ich das jetzt richtig verstanden habe, besteht bei diesen globalen Variablen die Gefahr, dass sie vom Compiler als extern interpretiert werden können - etwa bei Namensgleichheit und fehlender Dekleration in einem anderen Modul.
Die Gefahr besteht, Beispieldateien:
char *global = "one"; char *function(void) { return global; }
#include <stdio.h> extern char *function(void); char *global; int main(void) { global = "two"; puts(global); puts(function()); return 0; }
global
ist hier eine über beide Dateien ("compilation units") globale Variable. Man könnte erwarten, dass die beiden Variablen verschieden sind, weil es so aussieht, als würde in beiden Dateien Speicher für sie bereitgestellt; aber eben das ist nicht der Fall. Wenn man dabei ein Missverständnis befürchtet, sollte man in der zweiten Dateiglobal
entweder initialisieren (das ergibt hier einen Fehler beim Linken, wegen der Mehrfach-Definition) oder dasextern
dazuschreiben (dann ist klar, dass es nicht als Definition verstanden werden soll).Das erklärt teilweise, warum erfahrene C-Leute dazu tendieren, alle Variablen zu initialisieren. Es empfiehlt sich in diesem Sinn auch, das
extern
niemals wegzulassen.Falls aber zwei verschiedene, nur über die jeweilige Datei globale Variablen erwünscht sind, muss man sie, wie gesagt,
static
machen. Weil das der Regelfall ist, kann man sagen, dass es ein Fehler von C ist, globale Variablen per default global über alle Dateien zu machen.Warum ist das so? Ich kann mir vorstellen, dass es daran liegt, dass C als möglichst einfache Sprache gedacht ist, und man auf diese Weise solche Fragen des Linkens weitgehend aus der Syntax heraushalten kann, was den Compiler einfach und portabel hält.
-
mngbd, wieso machst Du unter fast jeden Beitrag einen Smiley?
-
Um frickys Andenken zu wahren?