Geltungsbereich von Variablen

Willemers Informatik-Ecke

Diese Seite basiert auf Inhalten aus meinem Buch "Einstieg in C++", seinerzeit erschienen im Verlag Galileo Computing. Das Buch ist inzwischen vergriffen.


Das Nachfolgebuch heißt C++. Der Einstieg und ist bei Wrox im Verlag Wiley-VCH erschienen.

Die Inhalte dieser Website unterliegen meinem Urheberrecht und dürfen trotz inhaltlicher Überschneidungen mit dem Buch dank der freundlichen Genehmigung des Verlags Wiley-VCH hier erscheinen.

Der Geltungsbereich von Variablen ist bereits bei der Beschreibung von Blöcken zur Sprache gekommen. Im Zusammenhang mit Funktionen bekommt der Geltungsbereich von Variablen eine besondere Bedeutung. Funktionen sollten so geschrieben werden, dass sie nach außen möglichst abgeschlossen sind. Das betrifft insbesondere die Variablen.

Globale Variablen

Variablen, die außerhalb aller Funktionen definiert sind, gelten in allen Funktionen. Beim Programmstart werden sie erzeugt und initialisiert. Beim Programmende werden sie zerstört. Bei ihrer Erzeugung werden globale Variablen zwar auf 0 gesetzt, dennoch sollten Sie auch globale Variablen initialisieren.

Deklaration

Auch für globale Variablen können Prototypen erstellt werden. Diese werden auch als Deklarationen angesprochen. Eine Deklaration unterscheidet sich von der Definition einer Variable darin, dass keinerlei Speicher für die Variable angelegt wird. Eine Deklaration macht eine Variable bekannt. Sie gibt also an, welchen Namen und welchen Typ sie hat. Darum ist jede Definition auch immer eine Deklaration. Eine Variable kann nur einmal definiert, aber beliebig oft deklariert werden, sofern die Deklarationen übereinstimmen.

extern

Eine Variable wird deklariert, ohne sie zu definieren, indem ihr das Schlüsselwort extern vorangestellt wird. Dabei muss extern nicht bedeuten, dass die Variable in einer anderen Datei stehen muss. Es heißt nur, dass dies keine Variablendefinition, sondern eine Deklaration ist.

[Deklaration einer Variablen]

enum tToken = { ... };

extern tToken aktuellesToken;

tError liesNummer(char *SourcePos)
{
    ...
    aktuellesToken = NUMBER;
    ...
}
...

tToken aktuellesToken = 0;

In den meisten Fällen sollten Sie es anstreben, globale Variablen zu vermeiden. Zu viele globale Variablen führen leicht dazu, dass die Übersicht und die Flexibilität eines Programms verloren geht. Die Informationsübergabe zwischen Funktionen gehört eigentlich in die Parameter. Und wenn Informationen über die Dauer eines Funktionsaufruf hinweg benötigt werden, sollte eine statische Variable verwendet werden. In einigen wenigen Fällen, in denen eine geringe und übersichtliche Zahl von globalen Variablen in nahezu jeder Funktion benötigt wird, kann der Einsatz globaler Variablen die Schnittstellen der Funktionen erheblich vereinfachen.

Lokale Variablen

Wird innerhalb eines Funktionsblocks eine Variable definiert, spricht man von einer lokalen Variablen. Außerhalb der Funktion kann nicht auf diese Variable zugegriffen werden. Sie wird erzeugt und initialisiert, wenn die Definition ausgeführt wird. Sie wird freigegeben, wenn der Block, in dem sie definiert ist, verlassen wird.

Hintergrund

Lokale Variablen werden auf dem
Stack angelegt. Dort liegen auch die Rücksprungadresse der aufrufenden Funktion und die Aufrufparameter. Dieser Speicher kann nach Verlassen der Funktion leicht wieder freigegeben werden. Im Gegensatz zu globalen Variablen wird der Speicherbereich des Stacks nicht vor Ihrer Benutzung mit 0 belegt. Der Speicherbereich dieser Variablen wird nach Verlassen des Blocks wieder freigegeben, aber nicht gelöscht. Aus diesem Grund sind nicht initialisierte lokale Variablen in einem undefinierten Zustand und mit hoher Wahrscheinlichkeit nicht 0. Wo immer es sich anbietet, sollten Sie lokale Variablen verwenden. Damit erreichen Sie, dass Funktionen keine unübersichtlichen Seiteneffekte auf globale Variablen haben. Der Speicher wird optimal genutzt, weil er von jeder Funktion nur so lange in Anspruch genommen wird, wie er gebraucht wird. Auch wenn dieses Problem Sie noch nicht berührt: Funktionen, die globale oder statische Variablen nutzen, können mit großer Wahrscheinlichkeit nicht parallel gestartet werden.

Parameter

Die Variablen der Parameterdefinition sind ebenfalls lokale Variablen. Sie werden auf dem Stack angelegt und bei Verlassen der Funktion wieder freigegeben. Sie werden durch die Aufrufparameter initialisiert. Lediglich Referenzvariablen sind nicht lokal, sondern ein Verweis auf die Variablen des Aufrufers.

Statische Variablen

Lokale Variablen können als statisch definiert werden.[1] Statische Variablen werden erzeugt und initialisiert, wenn ihre Definition zum ersten Mal durchlaufen wird. Die Variable und ihr Inhalt bleiben dann bis zum Ende des Programms erhalten. Um eine Variable als statische Variable zu definieren, wird der Definition das Schlüsselwort static vorangestellt.

Sichtbarkeit

Die Variable ist nur in dem Block sichtbar, in dem sie definiert wurde. Im Gegensatz zu einer globalen Variablen kann auf sie nicht von außerhalb zugegriffen werden. Eine statische Variable ist also das Mittel der Wahl, wenn eine Funktion eine Art Gedächtnis dafür braucht, was beim letzten Funktionsaufruf ausgeführt worden ist.

Beispiel

Im folgenden Beispiel werden die drei Variablenarten verglichen und das unterschiedliche Verhalten demonstriert.

[static-Variable]

#include <iostream>
using namespace std;

int globvar = 1;

void myfunc()
{
    static int statvar = 1;
    int locvar = 1;

    cout << "statisch: " << statvar;
    cout << " global: " << globvar;
    cout << " lokal: " << locvar << endl;
    statvar++;
    globvar++;
    locvar++;
}

int main()
{
    myfunc();
    myfunc();
    myfunc();
    locvar  = 8; // dies gibt einen Compiler-Fehler
    statvar = 8; // dies gibt einen Compiler-Fehler
    globvar = 8; // dies funktioniert
    myfunc();
    myfunc();
}

Die Funktion myfunc() arbeitet mit drei Variablen. Alle werden angezeigt und inkrementiert. Die Funktion wird vom Hauptprogramm dreimal aufgerufen. Dann versucht das Programm, den Variablen den Wert 8 zuzuweisen. Das wird bei locvar und bei statvar bereits vom Compiler unterbunden. Beide Variablen nur innerhalb der Funktion myfunc sichtbar sind. Schließlich wird myfunc noch zweimal aufgerufen. Nachdem die beiden Fehler aus dem Programm beseitigt wurden, kann das Programm gestartet werden.

statisch: 1 global: 1 lokal: 1
statisch: 2 global: 2 lokal: 1
statisch: 3 global: 3 lokal: 1
statisch: 4 global: 8 lokal: 1
statisch: 5 global: 9 lokal: 1

Lokale Variable

Im Beispiel ist das unterschiedliche Verhalten der verschiedenen Variablen zu sehen. Das Verhalten der lokalen Variablen locvar ist am einfachsten erklärt. Die Variable wird bei jedem Betreten der Funktion neu erzeugt und beim Verlassen wieder zerstört. Jedes Mal wird sie auch neu initialisiert. Darum enthält sie auch jedes Mal eine 1. Nach dem Inkrementieren hat sie innerhalb der Funktion noch den Wert 2. Aber das nützt ihr auch nichts. Sie wird beim Beenden der Funktion zerstört.

Globale Variable

Die globale Variable globvar wird beim Programmstart erzeugt und initialisiert. Innerhalb der Funktion wird sie inkrementiert und angezeigt. Da sie eine globale Variable ist, kann jede Funktion und darum natürlich die Hauptfunktion main() auf sie zugreifen. Das wird dadurch demonstriert, dass in main() nach dem dritten Funktionsaufruf von myfunc() den Wert auf 8 festsetzt. Bei den nächsten Aufrufen arbeitet die Funktion myfunc() mit diesem veränderten Wert weiter.

Statische Variable

Die statische Variable statvar verhält sich teilweise wie eine lokale und teilweise wie eine globale Variable. Wie eine lokale Variable ist sie gegen Zugriffe geschützt, die von außerhalb erfolgen. Niemand kann von außen versehentlich ihren Wert verändern. Andererseits verliert die statische Variable beim Verlassen der Funktion nicht ihren Wert. Die Initialisierung wird nur einmal ausgeführt, und zwar beim ersten Starten der Funktion.
[1]
Sie können auch globale Variablen als >>static<< definieren. Das hat aber eine völlig andere Bedeutung.


Informatik-Ecke Einstieg in C++ (C) Copyright 2005 Arnold Willemer