Offene Cee FAQ / Eine Globale Variable
StartSeite | OffeneCeeFAQ/ | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
| Inhaltsverzeichnis dieser Seite | |
|
|
Frage | |
Wie erzeugt man in C eine "globale Variable"?
Antwort (kompakte Variante) | |
Man schreibt ganz einfach Typ und Bezeichner außerhalb von jeder Funktion hin:
Hier wird eine Variable angelegt, und implizit 'globalisiert'.
Es wird wird Speicherplatz dem Typ entsprechend zugewiesen und der zugehörige
Bezeichner ist angegeben.
(Die Variable wird hier nicht explizit 'lokalisiert'.)
Bei Existenz einer solchen Variablendefinition kann man überall nach Verwendung des Schlüsselwortes extern diese 'globale' Variable benutzen:
| extern int A;
//...
c = A + b;
//... |
|
|
Hier werden mit Hilfe von »extern« Typ und Bezeichner einer 'globalen' Variablen
bekannt gemacht, eingeführt, deklariert.
Die obenstehende Definition
int A;
entspricht folglich quasi
public int A;
wobei es Schlüsselwörter wie »public global export« (explizite Globalisierung) in C allerdings nicht gibt.
| extern int A;
int main(void)
{
return A+6;
}
int A; |
|
|
Ohne die Bekanntmachung mittels extern int A; würde der Compiler eine Fehlermeldung
geben,
weil dann A bei Benutzung noch unbekannt wäre.
(Es versteht sich von selbst, daß speziell hier das int A; das extern int A; oben ersetzen könnte.
Das Anlegen ist nämlich selbstverständlich gleichzeitig auch eine Bekanntmachung für nachfolgenden Code. Es wäre hochgradig seltsam, wenn das nicht so wäre.)
static gibt innerhalb von Funktionsblöcken statische Lebensdauer an.
Bei anderen Verwendungen wird »static« zweckentfremdet, denn es wirkt dabei so
als ob es »local« hieße:
static int B;
erzeugt eine dateilokale Variable, die allerdings auch ohne »static« bereits statische
Lebensdauer hätte.
local int B;
wäre also klarer, um eine explizite Lokalisierung anzugeben.
(»local« gibt es nicht in C.)
Ein Fehler, der nicht selten vorkommt:
| char C[300];
//---------------------
extern char C[];
extern char C[300]; // Alternative
extern char *C; // Falsch!
|
|
|
Das ist kein Widerspruch zu:
| int main(int argc, char *argv[]);
int main(int argc, char **argv); // Alternative |
|
|
Denn bei Übergabe an main() wurde argv ja in einen Pointer auf argv[0] verwandelt.
Solch einen Vorgang gibt es bei public --> extern nicht.
Antwort (ausführliche Variante) | |
Es muß darauf hingewiesen werden, daß C (SpracheCee) den Begriff "globale Variable" an keiner Stelle verwendet. Dies aus gutem Grund, denn die Sprache unterscheidet zwischen den Objekten eines Programms und deren Bezeichner. Also ein Speicherplatz und der Name unter dem dieser Speicher angesprochen werden kann.
Zunächst muß geklärt werden, was mit dem Begriff Variable gemeint ist.
In erster Linie ist die Frage wohl so zu verstehen, daß der Name eines Objektes, auf das zugegriffen werden soll, "global" verfügbar sein soll.
Ein Name eines Objektes ist ein Bezeichner eines Objektes.
| 1: /* *** Datei a.c *** */
2:
3: int var = 5;
|
|
|
Wird ein Objekt, wie im oben stehenden Beispielcode, mit file scope definiert, so existiert dieses Objekt im gesamten Programm und während der gesamten Dauer der Programmausführung.
| 6.9.2 External object definitions
1 If the declaration of an identifier for an object has file scope and an initializer,
the declaration is an external definition for the identifier.
6.2.2 Linkages of identifiers
5 [...] If the declaration of an identifier for an object has file scope
and no storage-class specifier, its linkage is external.
6.2.4 Storage durations of objects
3 An object whose identifier is declared with external[...]linkage[...]has static storage duration.
Its lifetime is the entire execution of the program [...]
6.2.2 Linkages of identifiers
2 In the set of translation units and libraries that constitutes an entire program,
each declaration of a particular identifier with external linkage denotes
the same object or function. [...] |
|
|
Anders verhält sich der Bezeichner. Dessen Gültigkeitsbereich endet mit der Übersetzungseinheit (CeeÜbersetzungsEinheit?).
| 6.2.1 Scopes of identifiers
4 [...] If the declarator or type specifier that declares the identifier
appears outside of any block or list of parameters,
the identifier has file scope, which terminates at the end
of the translation unit. [...] |
|
|
Von einer "globalen Variablen" kann also hier nicht gesprochen werden, wenn global das gesamte Programm meint.
Hier ist lediglich das Objekt "global".
So scheitert der Versuch, die nachfolgend gezeigte Datei b.c zu übersetzen, auch wenn anschließend noch die Datei (deren Kompilat) a.c hinzugelinkt werden soll, die Datei a.c "gerade eben noch" vom Kompiler übersetzt wurde oder beide Dateien, beispielsweise mittels einer make-Datei, zu einem Projekt zusammengefasst werden.
| 1: /* *** Datei b.c *** */
2: #include<stdio.h>
3:
4: int main(void)
5: {
6: printf("%d\n", var);
7: return 0;
8: }
|
|
|
Dies bedeutet jedoch nicht, daß ein Objekt nicht im gesamten Programm ansprechbar[1] wäre.
Interessant im Zusammenhang mit der urspünglich gestellten Frage, ist wieder die Bindung (linkage) eines Bezeichners.
Sollen nun in verschiedenen Übersetzungseinheiten gleiche Bezeichner (Namen) das selbe Objekt ansprechen, so muß eine externe Bindung für alle diese Bezeichner gewählt werden.
| 5.1.1.1 Program structure
1 [...] The separate translation units of a program communicate by (for example) calls to functions
whose identifiers have external linkage, manipulation of objects whose identifiers
have external linkage, or manipulation of data files. Translation units may be separately
translated and then later linked to produce an executable program.
6.2.2 Linkages of identifiers
2 In the set of translation units and libraries that constitutes an entire program,
each declaration of a particular identifier with external linkage denotes
the same object or function. [...] |
|
|
Es kann also wie folgt vorgegangen werden:
- Es muß also ein Objekt definiert (CeeObjektDeklaration?) werden, das eine Lebensdauer hat, welche die gesamte Laufzeit des Programms umfasst (statische Lebensdauer) und
- alle (gleichen) Bezeichner dieses Objektes müssen externe Bindung haben.
Um einen Bezeichner zu einem Objekt mit externer Bindung und statischer Lebensdauer zu definieren, genügt es dem Bezeichner den Gültigkeitsbereich file scope zu geben und dabei keinen Speicherklassesmodifizierer anzugeben.
Die entsprechenden Stellen 6.2.2 Abs. 5, 6.2.4 Abs. 3 und 6.9.2 Abs. 1 aus dem Standard habe ich oben bereits zitiert.
Das nachfolgende Beispiel zeigt die Datei a.c mit der Erweiterung einer Funktion.
| 1: /* *** Datei a.c *** */
2: int var = 0; /* Definition eines Objektes mit dem Bezeichner var und exterer Bindung */
3:
4: void func(int i) {
5: var = i;
6: }
|
|
|
In der oben gezeigten Datei a.c werden die Bezeichner var und func deklariert.
Beide Bezeichner haben nach 6.2.2 Abs. 5 eine externe Bindung.
Soll nun von einer anderen Übersetzungseinheit auf das Objekt var oder die Funktion func zugegriffen werden, so müssen in diesen namensgleiche Deklarationen mit externer Bindung vorgenommen werden.
Die Deklarationen dürfen dabei keine neuen Objekte oder Funktionen erzeugen, es dürfen also keine Definitionen sein, da die Sprachspezifikation mehrere gleichnamige externe Objekte verbietet.
| 6.9 External definitions
5 [...] in the entire program there shall be exactly one external definition for the identifier; [...] |
|
|
Der folgende Quelltext zeigt eine Beispielimplementierung.
| 1: /* *** Datei b.c *** */
2: #include <stdio.h>
3: extern int var;
4: extern void func(int);
5:
6: int main(void)
7: {
8: printf("%d, ", var);
9: func(5);
10: printf("%d\n", var);
11: return 0;
12: }
|
|
|
- [InArbeit] Hier sollten noch Belege für die Verwendung von extern erfolgen.
Beachten Sie, daß in der Datei b.c lediglich der Bezeichner deklariert wurde.
Hier wurde kein Objekt angelegt.
Wird bei der Deklaration des Bezeichners var in der Zeile 3 auf den Speicherklassenmodifizierer extern verzichtet, so wird nach 6.9.2 Abs. 1 ein Objekt erstellt werden.
Dies hätte zur Folge, daß anschließend nicht mehr beide Dateien zu einem Programm verbunden werden können, da jetzt zwei Objekte var in diesem Programm existieren würden.
Der Modifizierer extern ist bei Funktionen jedoch nicht zwangsläufig notwendig.
Wird ein Bezeichner für eine Funktion deklariert und dabei kein Speicherklassenmodifizierer angegeben, so ist dies einer Angabe von extern gleichbedeutend. Zum Beleg sei der oben fehlende Teil von 6.2.2 Abs. 5 nachgereicht:
| 6.2.2 Linkages of identifiers
5 If the declaration of an identifier for a function has no storage-class specifier,
its linkage is determined exactly as if it were declared with the storage-class
specifier extern. [...] |
|
|
Wie die beiden Quelltexte übersetzt und das resultierende Programm gestartet werden kann, ist nachfolgend dargestellt[2].
| % cc a.c b.c
% ./a.out
0, 5
% _ |
|
|
Die Ausgabe des Programms zeigt, daß sowohl die Funktion main wie auch die Funktion func auf das gleiche Objekt über einen Bezeichner var zugreifen.
Abschließend kann also gesagt werden, dass C keine globale Namen kennt, sondern lediglich Objekte, die im gesamten Bereich der Programms ansprechbar sein können.
Soll auf ein solches Objekt von einer anderen Übersetzungseinheit zugegriffen werden, muß dort noch ein Bezeichner deklariert werden, der auf das Objekt verweist.
Was sonst noch zu diesem Thema gesagt werden sollte: | |
Bezeichner in einer Headerdatei zusammenfassen | |
Sollen Bezeichner in mehreren Übersetzungseinheiten verwendet werden, ist es ratsam, die Deklarationen in eine Header-Datei zu schreiben und diese dann in den gewünschten Quelltexten zu "includieren".
Dabei ist es irrelevant, ob die Bezeichner nun auf Variablen oder Funktionen verweisen.
Nachfolgend wird ein Vorgehen illustriert.
| 1: /* *** Datei a.h *** */
2: extern int var;
3: extern void func(int);
|
|
|
| 1: /* *** Datei a.c *** */
2: #include "a.h"
3:
4: int var=0;
5:
6: void func(int i) {
7: var = i;
8: }
|
|
|
| 1: /* *** Datei b.c *** */
2: #include <stdio.h>
3: #include "a.h"
4:
5: int main(void)
6: {
7: printf("%d, ", var);
8: func(5);
9: printf("%d\n", var);
10: return 0;
11: }
|
|
|
Es fällt auf, daß auch in der Datei a.c die Header-Datei a.h eingebunden wurde.
Dies ist eigendlich überflüssig, jedoch ermöglicht diese Vorgehensweise es dem Kompiler eventuelle Unstimmigkeiten zwischen den Deklarationen in der Header-Datei a.h und der tatsächlichen Definition in der Quelldatei a.c zu erkennen.
Bezeichner so lokal wie möglich definieren | |
Überlegen Sie, ob Sie einen Bezeichner wirklich in allen Übersetzungseinheiten benötigen.
Sollte ein Bezeichner lediglich von einer begrenzten Anzahl von Funktionen benötigt werden, jedoch nicht allgemein ansprechbar sein, so ist es ratsam ein Objekt zu definieren, dessen Bezeichner eine interner Bindung (internal linkage) hat.
Eine interne Bindung kann mit dem Speicherklassenmodifizierer static erreicht werden.
| 1: static int var;
2: extern void setVar(int x) { var = x; }
3: extern int getVar() { return var; }
|
|
|
Im eben gezeigten Beispiel haben lediglich die Funktionen eine externe Bindung.
Der Bezeichner var hat eine interne Bindung.
Dies hat zur Folge, daß
- dieses Objekt nicht von anderen Übersetzungseinheiten ansprechbar ist. Auch nicht mit einer namesgleichen extern-Deklaration.
- in anderen Übersetzungseinheiten ein anderes Objekt mit dem Bezeichner var definiert werden kann.
Diese Vorgehensweise ist nicht auf Variablen beschränkt.
Ebenso kann auch eine Funktion mit interner Bindung definiert werden, wenn diese nur in dieser Übersetzungseinheit benötigt wird.
- [InArbeit] -- Belege aus dem Standard angeben.
extern bedeutet nicht external linkage | |
Es soll nicht unerwähnt bleiben, daß die Angabe des Speichklassenmodifizierers extern nicht automatisch eine Deklaration eines Bezeichners mit externer Bindung zur Folge hat.
| 6.2.2 Linkages of identifiers
4 For an identifier declared with the storage-class specifier extern
in a scope in which a prior declaration of that identifier is visible,23)
if the prior declaration specifies internal or external linkage, the linkage
of the identifier at the later declaration is the same as the linkage
specified at the prior declaration. If no prior declaration is visible, or if the
prior declaration specifies no linkage, then the identifier has external linkage. |
|
|
So kann im nachfolgenden Beispiel die Funktion main in der Datei b.c nicht auf das Objekt var aus der Datei a.c zugreifen.
Stattdessen greift der Bezeichner, der in der Zeile 7 deklariert wird, auf das Objekt der gleichen Datei (mit interner Bindung) zu.
| 1: /* *** Datei: a.c *** */
2: int var = 5;
|
|
|
| 1: /* *** Datei: b.c *** */
2: #include <stdio.h>
3: static int var = 10;
4:
5: int main(void)
6: {
7: extern int var;
8: printf("%d\n", var);
9: return 0;
10: }
|
|
|
Beide Dateien lassen sich, wie schon oben gezeigt, übersetzen.
Das resultierende Programm gibt eine 10 und nicht eine 5 aus.
| Die Texte in den grauen Kästen sind Zitate aus '''ISO/IEC 9899:1999 (E)''', dem aktuell gültigen C-Standard. |
|
|
-- Claudio Carobolante
Resourcen | |
Diskussion | |
IMO sollte man das neu formulieren oder zumindest überarbeiten.
- Ein Anfang ist gemacht (ursprüngliche Variante) --Claudio Carobolante
Ich halte es für eine falsche Interpretation des Standard, was hier eingangs gesagt
wird. Die Aussage, C kennt keine globalen Variablen, halte ich für falsch.
Die Aussage, Der Standard verwendet den Begriff "Globale Variable" nicht, ist richtig.
- Ich habe kein Problem mit Deiner Formulierung und änder die ensprechende Passage gerne ab. --Claudio Carobolante
Interessant ist die Frage, was der Standard mit 'file scope' ganz genau meint,
und auf was er sich bei seinen diversen Absätzen genau bezieht.
Hier ist es nämlich erneut so, daß man mehrere Teile des Standard lesen muß, um überhaupt
zu einer korrekten Aussage kommen zu können.
Wenn man in einer Übersetzungseinheit eine Variable definiert, außerhalb von Funktionen
und ohne 'static', dann hat man garantiert das, was allgemein als 'Globale Variable'
bekannt ist. Lediglich es wird nicht unter Verwendung des Wortes 'global' im Standard beschrieben.
Mit einer solchen Variablen muß unbedingt eine Information einhergehen, die diese
Variable globalisiert, also für eventuell angegebene 'extern'-Deklarationen in eventuellen anderen Übersetzungseinheiten erreichbar macht.
- Du verwendest in den letzten beiden Absätzen den Begriff Variable. Du meinst das Objekt, auf welches ein Bezeichner verweist. Deine Aussagen sind im Bezug auf das Objekt richtig, jedoch machst Du nicht deutlich, daß auf ein Objekt nur über einen Bezeichner zugegriffen werden kann. Dieser muß jedoch in jeder "fremden" 'translation unit', wie Du richtig bemerkt hast, über extern erst definiert werden, bevor das Objekt in dieser 'translation unit' ansprechbar ist. Deine Aussagen sind also im Bezug zum Bezeichner, und auf den kommt es gerade Deinen so geliebten "verwirrten Anfänger" an, falsch. --Claudio Carobolante
- »Variable« ist ein Oberbegriff. Damit ist stets Speicherplatz und Bezeichner gemeint. Der Fragesteller fragt nach einer »Globalen Variablen«. Nach einer. Nach einer, auf die er im gesamten Programm zugreifen kann. Es ist ihm klar, daß er nur einen Speicherplatz will. Ebenso ist ihm klar, daß Speicherplatz stets durch Bezeichner identifiziert wird. Ohne Bezeichner müßte man ja die Adresse wissen und mittels *0xff3d0022= 12; zugreifen. Ich verwende den Begriff »Variable« nicht allein, isoliert, sondern: »...Variable definiert...« und »...solchen Variablen...«. Ich wüßte nicht, warum ich hier im Diskussionsabschnitt dieser Seite mehr erklären sollte, als ich es tat.
Wenn ich Kollegen in der Firma etwas erkläre und das genau gemäß Standard erkläre, dann wird geantwortet: "Herr Schellong, das will ich ich gar nicht wissen. Damit kann ich nichts anfangen. Sagen Sie mir einfach, wie man's hinschreibt!". Die Zusammenhänge erkennen meine Kollegen dann selbständig, nachdem ich sagte, wie man's schreibt. Die sind nämlich alle intelligente Pragmatiker.--hs
- Mit Variable ist hier wohl das Objekt, also der Speicherplatz, gemeint, denn der Bezeichner wird nicht "globalisiert"[3]. --Claudio Carobolante
- Nein, mit Variable ist grundsätzlich Variable gemeint. Wenn ich den Speicherplatz meinen würde, hätte ich nicht Variable geschrieben. Variable ist ein Oberbegriff und beinhaltet stets Speicher+Bezeichner!
Die Fußnote ist überflüssig, denn derjenige, der fragt, wie man in C eine globale Variable anlegt, will ja von vornherein das erreichen, was die Fußnote aussagt. Er weiß das also schon, und meint mit 'globale' eben genau das von vornherein.--hs
- Wenn Deine Aussage gleichermaßen für das Objekt, wie auch für den Bezeichner gelten soll, dann ist sie falsch. Wenn der Bezeichner 'globalisiert' werden würde, weshalb ist sollte dann noch ein extern int A; in anderen Übersetzungseinheiten nötig sein? Die Fußnote ist nötig, da hier schon gefragt wurde, was denn unter 'globalisiert' zu verstehen ist. --Claudio Carobolante
- Nein. Wenn zur Globalisierung einer Variablen es ausreicht, daß nicht alle ihre Subteile dazu globalisiert werden müssen, sondern nur ein Teil, dann wurde die Variable erfolgreich globalisiert. Ich habe nicht behauptet, daß Objekt als auch Bezeichner globalisiert werden müssen, um eine Variable zu globalisieren. Auf diese Einzelheiten sollte man eingangs auch gar nicht eingehen. Das steht im nachfolgenden Text dieser Seite "Eine Globale Variable" zur Genüge. Auch die Erklärung der Fußnote steht da jetzt doppelt. Ebenso die Erklärung der Bedeutung des Wortes "global" steht und stand dort, auf dieser Seite jetzt mehrfach vorhanden. Ich glaube langsam, es haben alle nur Langeweile und wollen mich ärgern...
Eine Globalisierung eines Bezeichners ist theoretisch möglich, jedoch unsinnig, weil extern int A; ebenfalls die notwendige Typinformation liefert. Bei Arrays sogar optional die Größe (sizeof). Außerdem würde die auf C-Ebene sichtbare extern-Deklaration fehlen. Desweiteren müßte der Compiler alle Übersetzungseinheiten jedesmal komplett sichten, um ohne extern auskommen zu können. Diese Betrachtung der einzelnen Globalisierung der Subteile einer Variablen ist folglich von vornherein Quatsch , verwirrt nur und ist der Sache dieser Seite nicht dienlich.--hs
Denn wenn das Resultat einer Übersetzungseinheit produziert wird, weiß der Compiler inhaltlich gar nichts von
eventuellen anderen Übersetzungseinheiten.
In der Regel passiert das auf Assemblerebene sogar mittels '.GLOBAL', '.globl', '.comm', 'COMMON', 'PUBLIC', etc.
Angesichts dessen
finde ich es für Anfänger verwirrend, wenn denen dauernd barsch entgegnet wird, in C gäbe
es keine globalen Variablen. --hs
- Ich war selbst einmal "Anfänger" und habe es trotzdem verstanden. Auch glaube ich nicht, in diesen beiden Punkten alleine zu stehen. Ich bin der Meinung, es ist besser, korrekt zu formulieren und den Anfängern hinreichend Intelligenz zu unterstellen. --Claudio Carobolante
Es kommt immer darauf an, wie man "global" definiert. Einmal pro Link-Einheit (DLL/so/...)? Einmal pro Prozess? Einmal pro User-Session? Einmal pro Maschine? Einmal pro LAN? Einmal weltweit?
- Es ist hier auf dieser Seite definiert. Nur eben ohne das Wort "global" zu benutzen.
Die Fragestellung geht übrigens weit am Standard vorbei. Der Standard beschreibt kein LAN, User-Session, etc. --hs
- Eine axiomatische Definition von "global" wäre wohl "innerhalb eines Adressraumes". Natürlich geht auch das an der Definition des Standards vorbei.
KategorieC KategorieCee
- [1] Einschränkend soll nicht unerwähnt bleiben, daß auf einen Bezeichner natürlich nicht vor seiner Deklaration zugegriffen werden kann. "Im gesamten Programm" ist also nicht richtig.
- [2] Unter Linux/UNIX.
- [3] Mit "globalisiert" ist in allen Übersetzungseinheiten, also im gesamten Programm, gemeint
StartSeite | OffeneCeeFAQ/ | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 29. November 2007 8:42 (diff))