vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 2

Tag 11

Gültigkeitsbereich e von Variablen

Am Tag 4, »Funktionen«, haben Sie gelernt, dass es einen Unterschied macht, ob eine Variable innerhalb oder außerhalb einer Funktion definiert wird. So haben Sie, ohne es zu wissen, bereits in diesem Kapitel mit dem Konzept des Gültigkeitsbereichs von Variablen Bekanntschaft gemacht - einem Konzept, das bei der Programmierung in C eine wichtige Rolle spielt. Heute lernen Sie:

Was ist ein Gültigkeitsbereich?

Als Gültigkeitsbereich einer Variablen bezeichnet man den Codeabschnitt, in dem man auf die Variable zugreifen kann - anders ausgedrückt, ein Abschnitt, in dem die Variable sichtbar ist. In C sind die Formulierungen »die Variable ist sichtbar« und »auf die Variable kann zugegriffen werden« austauschbar. Das Konzept der Gültigkeitsbereiche gilt für alle Arten von Variablen: einfache Variablen, Arrays, Strukturen, Zeiger etc. sowie für die symbolischen Konstanten, die mit dem Schlüsselwort const definiert wurden.

Der Gültigkeitsbereich legt auch die Lebensdauer einer Variablen fest: also wie lange eine Variable im Speicher existiert oder, anders ausgedrückt, wann der Speicher für eine Variable reserviert und freigegeben wird. Bevor wir uns ausführlicher mit Sichtbarkeit und Gültigkeitsbereichen beschäftigen, wollen wir uns ein kleines einführendes Beispiel anschauen.

Den Gültigkeitsbereichen nachgespürt

Betrachten Sie das Programm in Listing 11.1. Es definiert in Zeile 5 die Variable x, gibt in Zeile 11 mit printf() den Wert von x aus und ruft dann die Funktion wert_ausgeben() auf, um den Wert von x erneut auszugeben. Beachten Sie, dass der Funktion wert_ausgeben() der Wert von x nicht als Argument übergeben wurde. Die Funktion verwendet x einfach und übergibt die Variable an printf()(Zeile 19).

Listing 11.1: Innerhalb der Funktion wert_ausgeben() kann man auf die Variable x zugreifen.

1:  /* Beispiel zur Illustration von Gültigkeitsbereichen. */
2:
3: #include <stdio.h>
4:
5: int x = 999;
6:
7: void wert_ausgeben(void);
8:
9: int main(void)
10: {
11: printf("%d\n", x);
12: wert_ausgeben();
13:
14: return 0;
15: }
16:
17: void wert_ausgeben(void)
18: {
19: printf("%d\n", x);
20: }

999
999

Dies Programm lässt sich ohne Probleme kompilieren und ausführen. Im Folgenden nehmen wir eine kleine Änderung an dem Programm vor und verschieben die Definition der Variablen x in die main()-Funktion. Den abgeänderten Quellcode mit der Definition von x in Zeile 9 sehen Sie in Listing 11.2.

Listing 11.2: Innerhalb der Funktion wert_ausgeben() kann nicht auf die Variable x zugegriffen werden.

1:  /* Beispiel zur Illustration von Gültigkeitsbereichen. */
2:
3: #include <stdio.h>
4:
5: void wert_ausgeben(void);
6:
7: int main(void)
8: {
9: int x = 999;
10:
11: printf("%d\n", x);
12: wert_ausgeben();
13:
14: return 0;
15: }
16:
17: void wert_ausgeben(void)
18: {
19: printf("%d\n", x);
20: }

Wenn Sie versuchen, Listing 11.2 zu kompilieren, erzeugt der Compiler eine Fehlermeldung, die ungefähr folgenden Wortlaut hat:

list1102.c: In function 'wert_ausgeben':
list1102.c:19: 'x' undeclared (first use in this function)
list1102.c:19: (Each undeclared identifier is reported only once
list1102.c:19: for each function it appears in.)1

In einer Fehlermeldung gibt die Zahl nach dem ersten Doppelpunkt die Programmzeile an, in der der Fehler aufgetreten ist. In unserem Beispiel ist dies die Zeile 19 mit dem printf()-Aufruf in der Funktion wert_ausgeben().

Diese Fehlermeldung teilt Ihnen mit, dass die Variable x in der Funktion wert_ausgeben() in Zeile 19 nicht deklariert oder, mit anderen Worten, nicht sichtbar ist. Beachten Sie jedoch, dass der Aufruf von printf() in Zeile 11 keine Fehlermeldung ausgelöst hat. In diesem Teil des Programms, außerhalb von wert_ausgeben(), ist die Variable x sichtbar.

Der einzige Unterschied zwischen den Listings 11.1 und 11.2 ist die Position, an der die Variable x definiert wird. Durch die Verschiebung der Definition von x ändern Sie den Gültigkeitsbereich der Variablen. In Listing 11.1 wird x außerhalb von main() definiert und ist deshalb eine globale Variable, deren Gültigkeitsbereich das gesamte Programm ist. Auf diese Variable kann man sowohl in der Funktion main() als auch in der Funktion wert_ausgeben() zugreifen. In Listing 11.2 wird x innerhalb einer Funktion, hier main(), definiert und ist deshalb eine lokale Variable, deren Gültigkeitsbereich auf die Funktion beschränkt ist, in der sie deklariert wurde (in unserem Beispiel main()). Aus diesem Grund existiert x für wert_ausgeben() nicht und der Compiler erzeugt eine Fehlermeldung. Wir werden uns gleich noch näher mit den globalen und lokalen Variablen beschäftigen, aber zuerst sollten Sie verstehen, warum Gültigkeitsbereiche so wichtig sind.

Warum sind Gültigkeitsbereiche so wichtig?

Um zu verstehen, wie wichtig die Gültigkeitsbereiche von Variablen sind, rufen Sie sich bitte noch einmal die Diskussion über die strukturierte Programmierung von Tag 4 ins Gedächtnis. Der strukturierte Ansatz teilt das Programm, wie Sie sich vielleicht noch erinnern, in unabhängige Funktionen, die jeweils bestimmte Aufgaben ausführen. Dabei liegt die Betonung auf unabhängig. Wahre Unabhängigkeit setzt allerdings voraus, dass die Variablen einer jeden Funktion nicht durch den Code anderer Funktionen beeinflusst werden. Nur dadurch, dass Sie die Daten der einzelnen Funktionen voneinander getrennt halten, können Sie sicherstellen, dass die Funktionen ihre jeweiligen Aufgaben bewältigen, ohne von anderen Teilen des Programms Knüppel in den Weg gelegt zu bekommen. Indem Sie Variablen innerhalb von Funktionen definieren, können Sie, wie Sie bald lernen werden, die Variablen vor den anderen Teilen des Programms »verstecken«.

Eine vollständige Datenisolierung zwischen den Funktionen ist allerdings auch nicht immer erstrebenswert. Der Programmierer hat aber - wie Sie schnell erkennen werden - die Möglichkeit, durch Festlegung der Gültigkeitsbereiche den Grad der Isolierung selbst zu bestimmen.

Globale Variablen

Eine globale Variable ist eine Variable, die außerhalb aller Funktionen definiert wurde; das bedeutet auch außerhalb von main(). Bis jetzt waren die meisten Variablendefinitionen in diesem Buch globaler Natur und standen im Quellcode vor der Funktion main(). Globale Variablen werden manchmal auch als externe Variablen bezeichnet.

Globale Variablen, die Sie bei der Definition nicht explizit initialisieren (der Variablen einen Wert zuweisen), werden vom Compiler mit dem Wert 0 initialisiert.

Der Gültigkeitsbereich globaler Variablen

Der Gültigkeitsbereich einer globalen Variablen erstreckt sich über das ganze Programm. Das bedeutet, dass eine globale Variable für jede Funktion - einschließlich main() - sichtbar ist. So ist zum Beispiel die Variable x in Listing 11.1 eine globale Variable. Wie Sie nach Kompilation und Ausführung des Programms sehen konnten, ist x in beiden Funktionen des Programms (main() und wert_ausgeben()) sichtbar und wäre es auch für alle weiteren Funktionen, die Sie dem Programm hinzufügen würden.

Genau genommen ist es nicht ganz korrekt, zu behaupten, dass der Gültigkeitsbereich globaler Variablen das ganze Programm umfasst. Vielmehr besteht der Gültigkeitsbereich aus der Quelltextdatei, in der die Variablendefinition steht. Viele kleine bis mittlere C-Programme - und insbesondere die Programme, die wir zur Zeit aufsetzen - bestehen aber nur aus einer einzelnen Datei. In diesem Fall sind die beiden Definitionen für den Gültigkeitsbereich identisch.

Es ist aber auch möglich, den Quellcode eines Programms auf zwei oder mehr eigenständige Dateien zu verteilen. Wie dies geht und warum man das macht, erfahren Sie am Tag 20, »Compiler für Fortgeschrittene«. Dort lernen Sie, welcher Sonderbehandlung globale Variablen in diesen Situationen bedürfen.

Einsatzbereiche für globale Variablen

Bisher wurden in den Beispielprogrammen meist globale Variablen verwendet. In der Praxis sollte man den Einsatz von globalen Variablen jedoch möglichst vermeiden. Warum? Weil die Verwendung externer Variablen das Prinzip der modularen Unabhängigkeit verletzt, das Dreh- und Angelpunkt der strukturierten Programmierung ist. Hinter der modularen Unabhängigkeit steht der Gedanke, dass jede Funktion (und jedes Modul) eines Programms allen Code und Daten enthalten sollte, die sie für die Erledigung ihrer Aufgabe benötigt. Bei den relativ kleinen Programmen, die Sie bisher geschrieben haben, mag das vielleicht noch nicht so ins Gewicht fallen, aber mit zunehmend längeren und komplexeren Programmen kann ein übermäßiger Gebrauch von globalen Variablen zu Problemen führen.

Wann sollte man globale Variablen verwenden? Definieren Sie eine Variable nur dann als global, wenn alle oder zumindest die meisten Funktionen Ihres Programms auf die Variable zugreifen müssen. Symbolische Konstanten, die mit dem Schlüsselwort const definiert werden, sind häufig gute Kandidaten für den globalen Status. Wenn nur einige Ihrer Funktionen Zugriff auf eine Variable benötigen, übergeben Sie die Variable der Funktion als Argument, anstatt Sie global zu definieren.

Das Schlüsselwort extern

Wenn eine Funktion eine globale Variable verwendet, ist es guter Programmierstil, die Variable innerhalb der Funktion mit dem Schlüsselwort extern zu deklarieren. Die Deklaration hat dann folgende Form:

extern typ name;

Dabei steht typ für den Variablentyp und name für den Variablennamen. So würden Sie zum Beispiel in Listing 11.1 die Deklaration von x in die Funktionen main() und wert_ausgeben() mit aufnehmen. Das resultierende Programm sehen Sie in Listing 11.3.

Listing 11.3: Die globale Variable x wird in den Funktionen main() und wert_ausgeben() als extern deklariert.

1:  /* Beispiel für die Deklaration externer Variablen. */
2:
3: #include <stdio.h>
4:
5: int x = 999;
6:
7: void wert_ausgeben(void);
8:
9: int main(void)
10: {
11: extern int x;
12:
13: printf("%d\n", x);
14: wert_ausgeben();
15:
16: return 0;
17: }
18:
19: void wert_ausgeben(void)
20: {
21: extern int x;
22: printf("%d\n", x);
23: }

999
999

Dieses Programm gibt den Wert von x zweimal aus. Zuerst in Zeile 13 als Teil von main() und dann in Zeile 22 als Teil von wert_ausgeben(). Zeile 5 definiert x als eine Variable vom Typ int mit dem Wert 999. Die Zeilen 11 und 21 deklarieren x als extern int. Beachten Sie den Unterschied zwischen einer Variablendefinition, die Speicher für die Variable reserviert, und einer Deklaration als extern. Letztere besagt: »Diese Funktion verwendet eine globale Variable, deren Typ und Name woanders definiert sind.« In diesem Fall wird die extern-Deklaration eigentlich nicht benötigt - das Programm würde sich genauso gut ohne die Zeilen 11 und 21ausführen lassen. Wäre jedoch die Funktion wert_ausgeben() in einem anderem Code-Modul (einer anderen Quelltextdatei) untergebracht als die globale Deklaration der Variablen x (in Zeile 5), dann wäre die Deklaration mit extern erforderlich.

Lokale Variablen

Eine lokale Variable ist eine Variable, die innerhalb einer Funktion definiert ist. Der Gültigkeitsbereich einer lokalen Variablen ist auf die Funktion, in der sie definiert wurde, beschränkt. Am Tag 4 wurden bereits lokale Variablen beschrieben - einschließlich ihrer Definition und ihrer Vorteile. Lokale Variablen werden nicht automatisch vom Compiler mit 0 initialisiert. Wenn Sie lokale Variablen nicht bei der Definition initialisieren, erhalten Sie einen undefinierten oder »unbrauchbaren« Wert. Sie müssen lokalen Variablen also explizit einen Wert zuweisen, bevor Sie sie das erste Mal verwenden.

Variablen können auch lokal zu der Funktion main() sein. Dies ist für x in Listing 11.2 der Fall. Dort ist die Variable x innerhalb von main() definiert, und beim Kompilieren und Ausführen des Programms können Sie feststellen, dass sie auch nur innerhalb von main() sichtbar ist.

Was Sie tun sollten

Was nicht

Verwenden Sie möglichst lokale Variablen, vor allem für Schleifenzähler und ähnliche Aufgaben.

Verwenden Sie lokale Variablen, um die Werte, die den Variablen zugewiesen werden, vom Rest des Programms getrennt zu halten.

Deklarieren Sie Variablen, die nicht von der Mehrzahl der Programmfunktionen benötigt werden, nicht als global.

Statische kontra automatische Variablen

Lokale Variablen sind standardmäßig auto.2 Das bedeutet, dass lokale Variablen jedes Mal neu erzeugt werden, wenn die Funktion aufgerufen wird, und beim Verlassen der Funktion wieder zerstört werden. Mit anderen Worten: Eine automatische Variable verliert ihren Wert zwischen den Aufrufen an die Funktion, in der sie definiert ist.

Angenommen Ihr Programm enthält eine Funktion mit einer lokalen Variablen x. Weiterhin angenommen, die Funktion weist der Variablen x beim ersten Aufruf den Wert 100 zu. Jetzt geht die Ausführung wieder zurück zum aufrufenden Programm und die Funktion wird später wieder aufgerufen. Ist in der Variablen x immer noch der Wert 100 abgelegt? Nein. Die erste Instanz der Variablen x wurde zerstört, als die Programmausführung die Funktion nach dem ersten Aufruf wieder verlassen hat. Als dann die Funktion erneut aufgerufen wurde, wurde eine neue Instanz von x erzeugt. Das x in seiner alten Form existiert nicht mehr.

Wie ist zu verfahren, wenn die Funktion den Wert einer lokalen Variablen zwischen ihren Aufrufen beibehalten soll? Nehmen Sie an, Sie hätten eine Druckfunktion, die sich die bereits an den Drucker gesendete Zeilenzahl merken soll, weil sie daran erkennen kann, wann es notwendig ist, einen neue Seite zu starten. Damit eine lokale Variable ihren Wert zwischen den Aufrufen behält, müssen Sie sie mit dem Schlüsselwort static als statisch definieren. Zum Beispiel:

void funk1(int x)
{
static int a;
/* hier steht weiterer Code */
}

Listing 11.4 verdeutlicht den Unterschied zwischen automatischen und statischen lokalen Variablen.

Listing 11.4: Der Unterschied zwischen automatischen und statischen lokalen Variablen.

1:  /* Beispiel für automatische und statische Variablen. */
2: #include <stdio.h>
3: void funk1(void);
4: int main(void)
5: {
6: int count;
7:
8: for (count = 0; count < 20; count++)
9: {
10: printf("In Durchlauf %d: ", count);
11: funk1();
12: }
13:
14: return 0;
15: }
16:
17: void funk1(void)
18: {
19: static int x = 0;
20: int y = 0;
21:
22: printf("x = %d, y = %d\n", x++, y++);
23: }

In Durchlauf 0: x = 0, y = 0
In Durchlauf 1: x = 1, y = 0
In Durchlauf 2: x = 2, y = 0
In Durchlauf 3: x = 3, y = 0
In Durchlauf 4: x = 4, y = 0
In Durchlauf 5: x = 5, y = 0
In Durchlauf 6: x = 6, y = 0
In Durchlauf 7: x = 7, y = 0
In Durchlauf 8: x = 8, y = 0
In Durchlauf 9: x = 9, y = 0
In Durchlauf 10: x = 10, y = 0
In Durchlauf 11: x = 11, y = 0
In Durchlauf 12: x = 12, y = 0
In Durchlauf 13: x = 13, y = 0
In Durchlauf 14: x = 14, y = 0
In Durchlauf 15: x = 15, y = 0
In Durchlauf 16: x = 16, y = 0
In Durchlauf 17: x = 17, y = 0
In Durchlauf 18: x = 18, y = 0
In Durchlauf 19: x = 19, y = 0

Dieses Programm enthält eine Funktion, funk1(), die eine statische und eine automatische lokale Variable definiert und initialisiert. Diese Funktion befindet sich in den Zeilen 17 bis 23. Bei jedem Funktionsaufruf werden beide Variablen auf dem Bildschirm ausgegeben und inkrementiert (Zeile 22). Die main()-Funktion in den Zeilen 4 bis 15 enthält eine for-Schleife (Zeilen 8 bis 12), die eine Nachricht ausgibt (Zeile 10) und dann funk1() aufruft (Zeile 11). Die for-Schleife wird 20-mal durchlaufen.

Schauen Sie sich die Ausgabe an. Wie Sie sehen, erhöht sich der Wert der statischen Variablen x bei jedem Durchlauf, da sie ihren Wert zwischen den Aufrufen beibehält. Die automatische Variable y dagegen wird mit jedem Aufruf wieder zu 0 initialisiert, so dass sich ihr Wert nicht erhöhen kann.

Dieses Programm zeigt auch, wie unterschiedlich die explizite Variableninitialisierung gehandhabt wird (das heißt, wenn eine Variable bei der Definition initialisiert wird). Eine statische Variable wird nur beim ersten Aufruf der Funktion initialisiert. Danach merkt sich das Programm diesen Wert für die weiteren Aufrufe und initialisiert sie nicht noch einmal. Statt dessen behält die Variable den Wert, den Sie beim Verlassen der Funktion hatte. Eine automatische Variable hingegen wird bei jedem Funktionsaufruf mit dem spezifizierten Wert initialisiert.

Wenn Sie selbst ein wenig mit automatischen Variablen experimentieren, erhalten Sie unter Umständen Ergebnisse, die den hier ausgeführten Regeln widersprechen. Wenn Sie zum Beispiel Listing 11.4 so ändern, dass die zwei lokalen Variablen nicht bei der Definition initialisiert werden, lautet die Funktion funk1() in den Zeilen 17 bis 23:

17: void funk1(void)
18: {
19: static int x;
20: int y;
21:
22: printf("x = %d, y = %d\n", x++, y++);
23: }

Wenn Sie dieses geänderte Programm ausführen, kann es durchaus sein, dass der Wert von y mit jedem Durchlauf um eins erhöht wird. Bedeutet dies, dass y seinen Wert zwischen den Funktionsaufrufen beibehält, obwohl es eine automatische lokale Variable ist? Ist alles, was Sie bisher über automatische Variablen und den Verlust ihrer Werte gelesen haben, nur »Käse«?

Nein, alles was Sie bisher gelernt haben, ist wahr (glauben Sie mir!). Wenn Sie feststellen, dass eine automatische Variable ihren Wert über wiederholte Funktionsaufrufe hinweg beibehält, ist das reiner Zufall. Folgendes könnte passiert sein: Jedes Mal, wenn die Funktion aufgerufen wird, wird ein neues y erzeugt. Der Compiler kann allerdings unter Umständen für das neue y die gleiche Speicherstelle verwenden, wie für das y im alten Funktionsaufruf. Wenn y nicht explizit von der Funktion initialisiert wird, kann diese Speicherstelle noch den Wert enthalten, den y während des vorherigen Aufrufs hatte. Man hat dadurch den Eindruck, als ob die Variable ihren alten Wert behalten hätte, aber es ist eigentlich nur Zufall. Sie können sich aber definitiv nicht darauf verlassen, dass dies jedes Mal der Fall ist.

Da lokale Variablen standardmäßig auto sind, braucht dies nicht in der Variablendefinition angegeben zu werden. Wenn Sie möchten, können Sie das Schlüsselwort auto in der Definition vor die Typangabe setzen:

void funk1(int y)
{
auto int count;
/* Hier steht weiterer Code */
}

Der Gültigkeitsbereich von Funktionsparametern

Variablen, die in der Parameterliste des Funktions-Headers deklariert werden, haben einen lokalen Gültigkeitsbereich. Betrachten wir dazu folgendes Beispiel:

void funk1(int x)
{
int y;
/* Hier steht weiterer Code */
}

Sowohl x als auch y sind lokale Variablen, deren Gültigkeitsbereich sich über die ganze Funktion funk1() erstreckt. Der Anfangswert von x ist der Wert, der der Funktion vom aufrufenden Programm übergeben wurde. In der Funktion können Sie x wie jede andere beliebige Variable verwenden.

Da Parametervariablen immer mit dem Wert initialisiert werden, der ihnen als Argument übergeben wird, macht es keinen Sinn, Begriffe wie »statisch« oder »automatisch« auf sie anzuwenden.

Statische globale Variablen

Sie können auch globale Variablen als statisch definieren. Dazu müssen Sie das Schlüsselwort static in die Definition mit aufnehmen:

static float rate;

int main(void)
{
/* Hier steht weiterer Code */
}

Der Unterschied zwischen einer normalen und einer statischen globalen Variablen liegt in ihrem Gültigkeitsbereich. Eine normale globale Variable ist für alle Funktionen in der Datei sichtbar und kann von den Funktionen anderer Dateien verwendet werden. Eine statische globale Variable ist hingegen nur für die Funktionen in der eigenen Datei sichtbar, und das auch nur ab dem Punkt ihrer Definition.

Diese Unterschiede betreffen verständlicherweise hauptsächlich Programme, deren Quellcode auf zwei oder mehr Dateien verteilt ist. Doch auf dieses Thema gehen wir noch ausführlich am Tag 20 ein.

Registervariablen

Mit dem Schlüsselwort register können Sie den Compiler auffordern, eine automatische lokale Variable in einem Prozessorregister anstatt im Arbeitsspeicher abzulegen. Was aber ist ein Prozessorregister und welche Vorteile ergeben sich, wenn man dort Variablen ablegt?

Die CPU (Central Processing Unit) Ihres Computers verfügt über wenige eigene Speicherstellen, so genannte Register. In diesen CPU-Registern finden die eigentlichen Datenoperationen, wie Addition und Division, statt. Um Daten zu manipulieren, muss die CPU die Daten aus dem Speicher in seine Register laden, die Manipulationen vornehmen und dann die Daten wieder zurück in den Speicher schreiben. Das Verschieben der Daten von und in den Speicher bedarf einer bestimmten Zeit. Wenn Sie eine bestimmte Variable im Register selbst ablegen, könnten die Manipulationen mit dieser Variablen viel schneller ablaufen.

Wenn Sie in der Definition einer automatischen Variablen das Schlüsselwort register verwenden, bitten Sie den Compiler, diese Variable in einem Register zu speichern. Sehen Sie dazu folgendes Beispiel:

void funk1(void)
{
register int x;
/* Hier steht weiterer Code */
}

Achten Sie auf meine Wortwahl. Ich sagte bitten und nicht befehlen. In Abhängigkeit von dem restlichen Code des Programms ist unter Umständen kein Register für die Variable verfügbar. Wenn kein Register zur Verfügung steht, behandelt der Compiler die Variable wie eine normale automatische Variable. Mit anderen Worten, das Schlüsselwort register ist ein Vorschlag und kein Befehl. Der Nutzen der register- Speicherklasse ist am größten für Variablen, die von der Funktion häufig verwendet werden - wie zum Beispiel Zählervariablen für Schleifen.

Das Schlüsselwort register kann nur mit einfachen numerischen Variablen verwendet werden und nicht mit Arrays oder Strukturen. Auch ist es nicht möglich, es zusammen mit statischen oder externen Speicherklassen zu verwenden. Sie können keinen Zeiger auf eine Registervariable definieren.

Was Sie tun sollten

Was nicht

Initialisieren Sie lokale Variablen, oder Sie können nicht sicher sein, welchen Wert die Variablen anfänglich haben.

Initialisieren Sie globale Variablen, obwohl diese standardmäßig mit 0 initialisiert werden. Wenn Sie sich angewöhnen, Ihre Variablen stets zu initialisieren, vermeiden Sie Probleme, wie sie beispielsweise entstehen, wenn die Initialisierung lokaler Variablen vergessen wird.

Übergeben Sie Daten, die nur in einigen wenigen Funktionen benötigt werden, als Funktionsparameter, anstatt sie global zu deklarieren.

Verwenden Sie keine Registervariablen für nichtnumerische Werte, Strukturen oder Arrays.

Lokale Variablen und die Funktion main()

Alles, was bisher über lokale Variablen gesagt wurde, gilt für main() genauso wie für alle anderen Funktionen. Genau genommen ist main() eine Funktion wie jede andere. Die Funktion main() wird aufgerufen, wenn das Programm von Ihrem Betriebssystem gestartet wird. Nach Programmende geht die Steuerung wieder von main() auf das Betriebssystem über.

Das bedeutet, dass lokale Variablen, die in main() definiert sind, zu Beginn des Programms mit einer Lebensdauer erzeugt werden, die endet, wenn auch das Programm zu Ende ist. Die Vorstellung einer statischen lokalen Variablen, die ihren Wert zwischen den Aufrufen von main() behält, ergibt hier keinen Sinn: Eine Variable kann nicht zwischen Programmausführungen existent bleiben. Deshalb gibt es in main() keinen Unterschied zwischen automatischen und statischen lokalen Variablen. Sie können eine lokale Variable in main() als statisch definieren, aber es hat keine Auswirkung.

Was Sie tun sollten

Was nicht

Merken Sie sich, dass die Funktion main() in vielerlei Hinsicht den anderen Funktionen gleichgestellt ist.

Deklarieren Sie keine statischen Variablen in main(), da Sie damit nichts gewinnen.

Welche Speicherklassen sollten Sie verwenden?

Wenn Sie vor der Entscheidung stehen, welche Speicherklasse Sie für bestimmte Variablen in Ihren Programmen verwenden sollen, könnte Tabelle 11.1 eine Hilfe sein. Dort finden Sie eine Übersicht über die fünf in C zur Verfügung stehenden Speicherklassen.

Speicherklasse

Schlüsselwort

Lebensdauer

Definitionsort

Gültigkeitsbereich

Automatisch

-1

temporär

in einer Funktion

lokal

Statisch

static

temporär

in einer Funktion

lokal

Register

register

temporär

in einer Funktion

lokal

Extern

-2

permanent

außerhalb der Funktionen

global (alle Dateien)

Extern

static

permanent

außerhalb der Funktionen

global (eine Datei)

Tabelle 11.1: Die fünf Speicherklassen für Variablen.

1

    Das Schlüsselwort auto ist optional.

2

    Das Schlüsselwort extern wird in Funktionen dazu verwendet, eine statische globale Variable zu deklarieren, die woanders definiert ist.

Wenn Sie eine Speicherklasse wählen müssen, sollten Sie sich wann immer möglich für die automatische Speicherklasse entscheiden und andere Speicherklassen nur bei Bedarf verwenden. Am besten halten Sie sich an die folgenden Regeln:

Lokale Variablen und Blöcke

Bis jetzt war die heutige Lektion auf die Diskussion von Variablen beschränkt, die lokal zu einer Funktion sind. So werden lokale Variablen normalerweise auch verwendet. Es gibt auch die Möglichkeit, Variablen lokal zu einem beliebigen Programmblock (das heißt einem beliebigem Programmabschnitt in geschweiften Klammern) zu deklarieren. Wenn Sie Variablen innerhalb eines Blocks deklarieren, müssen Sie daran denken, dass die Deklarationen vor den Anweisungen des Blocks stehen müssen. Listing 11.5 zeigt hierzu ein Beispiel.

Listing 11.5: Definition lokaler Variablen in einem Programmblock.

1:  /* Beispiel für lokale Variablen innerhalb eines Blocks. */
2:
3: #include <stdio.h>
4:
5: int main(void)
6: {
7: /* Definiert eine Variable lokal zu main(). */
8:
9: int count = 0;
10:
11: printf("\nAußerhalb des Blocks, count = %d", count);
12:
13: /* Anfang eines Blocks. */
14: {
15: /* Definiert eine Variable lokal zum Block. */
16:
17: int count = 999;
18: printf("\nInnerhalb des Blocks, count = %d", count);
19: }
20:
21: printf("\nErneut außerhalb des Blocks, count = %d\n", count);
22: return 0;
23: }

Außerhalb des Blocks, count = 0
Innerhalb des Blocks, count = 999
Erneut außerhalb des Blocks, count = 0

Dieses Programm zeigt deutlich, dass die Variable count, die innerhalb des Blocks definiert wurde, unabhängig von der Variablen count ist, die außerhalb des Blocks definiert wurde. Zeile 9 definiert count als eine Variable vom Typ int und initialisiert sie mit 0. Da die Variable zu Beginn von main() deklariert wurde, kann sie die ganze main()-Funktion hindurch verwendet werden. Die Ausgabe in Zeile 11 bestätigt, dass die Variable count mit 0 initialisiert wurde. Die Zeilen 14 bis 19 deklarieren einen Block und innerhalb des Blocks eine neue Variable count vom Typ int. Diese count- Variable wird in Zeile 17 mit 999 initialisiert. Zeile 18 gibt den Wert der count- Variablen des Blocks aus. Da der Block in Zeile 19 endet, gibt die printf()- Anweisung in Zeile 21 die originale count-Variable aus, die in Zeile 9 von main() deklariert wurde.

Variablen, die lokal zu einem Block sind, werden in der C-Programmierung nicht allzu häufig verwendet, und vielleicht werden Sie sie nie benötigen. Vermutlich werden sie vor allem von Programmierern eingesetzt, die versuchen, ein Problem in einem Programm zu isolieren. Sie können verdächtige Codeabschnitte mit Hilfe von geschweiften Klammern vorübergehend isolieren und in dem Block dann lokale Variablen definieren, die Ihnen helfen, das Problem aufzuspüren. Ein weiterer Vorteil ist der, dass Sie die Variablendeklaration/-initialiserung näher an den Ort stellen können, wo die Variablen benötigt werden, so dass das Programm transparenter wird.

Was Sie tun sollten

Was nicht

Wenn Sie Hilfsvariablen zum Aufspüren eines Problems benötigen, deklarieren Sie diese als (temporäre) Variablen zu Beginn eines Blocks.

Versuchen Sie nicht, Variablendefinitionen irgendwo anders als zu Beginn einer Funktion oder zu Beginn eines Blocks aufzusetzen.

Definieren Sie keine Variablen am Anfang eines Blocks, es sei denn, es macht das Programm klarer.

Zusammenfassung

Das heutige Kapitel behandelte die Begriffe Gültigkeitsbereich und Lebensdauer im Zusammenhang mit den Speicherklassen der C-Variablen. Jede C-Variable - egal ob es sich dabei um eine einfache Variable, ein Array, eine Struktur oder etwas anderes handelt - verfügt über eine bestimmte Speicherklasse, die zwei Dinge festlegt: ihren Gültigkeitsbereich (wo im Programm die Variable sichtbar ist) und ihre Lebensdauer (wie lange die Variable im Speicher verbleibt).

Die korrekte Verwendung der Speicherklassen ist ein wichtiger Aspekt der strukturierten Programmierung. Indem Sie Variablen so weit es geht lokal in den Funktionen definieren, in denen sie verwendet werden, fördern Sie das Prinzip der Unabhängigkeit der Funktionen untereinander. Variablen sollten grundsätzlich der automatischen Speicherklasse angehören, es sei denn, es gibt einen besonderen Grund, um sie als global oder statisch zu definieren.

Fragen und Antworten

Frage:
Wenn man globale Variablen überall im Programm verwenden kann, warum sind dann nicht einfach alle Variablen global?

Antwort:
Mit zunehmender Größe werden Ihre Programme immer mehr Variablen enthalten. Globale Variablen belegen während der ganzen Ausführung des Programms Speicher, wohingegen automatische lokale Variablen nur so lange Speicher belegen, wie die Funktion, in der sie definiert sind, ausgeführt wird. Demzufolge können Sie durch die Verwendung von lokalen Variablen Speicher sparen. Wichtiger jedoch ist, dass Sie mit lokalen Variablen die Möglichkeiten ungewollter Interaktionen zwischen den verschiedenen Teilen des Programms stark reduzieren. Damit reduzieren Sie gleichzeitig die Zahl der Programmfehler und halten sich an das Prinzip der strukturierten Programmierung.

Frage:
Am Tag 10, »Strukturen,« wurde behauptet, dass der Gültigkeitsbereich einen Einfluss auf eine Strukturinstanz aber nicht auf einen Strukturnamen oder -rumpf hat. Warum hat der Gültigkeitsbereich keinen Einfluss auf Strukturnamen oder - rümpfe?

Antwort:
Wenn Sie eine Struktur ohne Instanzen deklarieren, erzeugen Sie eine Schablone, aber deklarieren keine Variablen. Erst wenn Sie eine Instanz dieser Struktur erzeugen, deklarieren Sie eine Variable, die Speicher belegt und einen Gültigkeitsbereich hat. Aus diesem Grund können Sie eine Struktur außerhalb aller Funktionen deklarieren, ohne dass dies Auswirkung auf den Speicher hat. Viele Programmierer legen häufig verwendete Strukturdefinitionen (Name und Rumpf) in den Header-Dateien ab und binden diese Header-Dateien dann ein, wenn Sie eine Instanz der Struktur erzeugen müssen. (Header-Dateien werden am Tag 20 behandelt).

Frage:
Woran erkennt der Computer den Unterschied zwischen einer globalen und einer lokalen Variable, die beide den gleichen Namen tragen?

Antwort:
Die Antwort auf diese Frage geht über den Rahmen dieses Kapitels hinaus. Wichtig dabei ist eins: Wenn eine lokale Variable mit dem gleichen Namen wie eine globale Variable deklariert wird, ignoriert das Programm die globale Variable so lang wie die lokale Variable gültig ist (üblicherweise also innerhalb der Funktion, in der die Variable definiert ist).

Frage:
Kann ich eine lokale und eine globale Variable mit dem gleichen Namen, aber mit unterschiedlichen Variablentypen deklarieren?

Antwort:
Ja. Wenn Sie eine lokale Variable mit dem gleichen Namen wie eine globale Variable deklarieren, erhalten Sie dadurch eine vollständig neue Variable. Das bedeutet, dass Sie für die Variable einen beliebigen Typ verwenden können. Sie sollten jedoch sorgfältig sein, wenn Sie globale und lokale Variablen mit gleichem Namen deklarieren. Einige Programmierer versehen alle globalen Variablennamen mit einem »g« für global (zum Beispiel gCount anstelle von Count). Damit wird aus dem Quelltext gleich ersichtlich, welche Variablen global und welche lokal sind.

Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, sowie Übungen, die Sie anregen sollen, das Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Die Lösungen zu den Fragen und den Übungen finden Sie in Anhang C.

Quiz

  1. Was versteht man unter einem Gültigkeitsbereich?
  2. Was ist der wichtigste Unterschied zwischen einer lokalen Speicherklasse und einer globalen Speicherklasse?
  3. Inwiefern beeinflusst die Position der Variablendefinition deren Speicherklasse?
  4. Wie lauten bei der Definition einer lokalen Variablen die zwei Optionen für die Lebensdauer der Variablen?
  5. Sowohl automatische als auch statische lokale Variablen können bei der Definition initialisiert werden. Wann werden die Initialisierungen ausgeführt?
  6. Wahr oder falsch: Eine Registervariable wird immer in einem Register abgelegt.
  7. Welchen Wert enthält eine nicht initialisierte globale Variable?
  8. Welchen Wert enthält eine nicht initialisierte lokale Variable?
  9. Was gibt Zeile 21 aus Listing 11.5 aus, wenn die Zeilen 9 und 11 entfernt werden? Denken Sie erst darüber nach und führen Sie dann das geänderte Programm aus, um festzustellen, was passiert.
  10. Wenn sich eine Funktion den Wert einer lokalen Variablen vom Typ int zwischen den Aufrufen merken soll, wie muss dann die Variable deklariert werden?
  11. Was bewirkt das Schlüsselwort extern?
  12. Was bewirkt das Schlüsselwort static?

Übungen

  1. Deklarieren Sie eine Variable, die in einem CPU-Register abgelegt werden soll.
  2. Korrigieren Sie Listing 11.2, so dass keine Fehlermeldung mehr auftritt. Verwenden Sie keine globalen Variablen.
  3. Schreiben Sie ein Programm, das eine globale Variable var vom Typ int deklariert. Initialisieren Sie var mit einem beliebigen Wert. Das Programm soll den Wert von var in einer Funktion (nicht main()) ausgeben. Müssen Sie der Funktion var als Parameter übergeben?
  4. Ändern Sie das Programm aus Übung 3. Anstatt var als globale Variable zu deklarieren, deklarieren Sie var als lokale Variable in main(). Das Programm soll var jedoch immer noch in einer separaten Funktion ausgeben. Müssen Sie der Funktion var als Parameter übergeben?
  5. Kann ein Programm eine globale und eine lokale Variable mit gleichem Namen enthalten? Schreiben Sie ein Programm, das eine globale und eine lokale Variable mit dem gleichen Namen verwendet, um Ihre Antwort zu bestätigen.
  6. FEHLERSUCHE: Was stimmt nicht mit dem folgenden Code? (Hinweis: Achten Sie darauf, wo die Variablen deklariert sind.)
    void eine_beispiel_funktion( void )
    {
    int ctr1;

    for ( ctr1 = 0; ctr1 < 25; ctr1++ )
    printf( "*" );

    puts( "\nDies ist eine Beispielfunktion" );
    {
    char sternchen = '*';
    puts( "\nEs gibt ein Problem\n" );
    for ( int ctr2 = 0; ctr2 < 25; ctr2++ )
    {
    printf( "%c", sternchen);
    }
    }
    }
  7. FEHLERSUCHE: Was ist falsch an folgendem Code?
    /*Zählt die Anzahl der geraden Zahlen zwischen 0 und 100. */

    #include <stdio.h>

    int main(void)
    {
    int x = 1;
    static int anzahl = 0;

    for (x = 0; x < 101; x++)
    {
    if (x % 2 == 0) /* wenn x gerade ist...*/
    anzahl++;.. /*addiere 1 zu anzahl.*/

    }

    printf("Es gibt %d gerade Zahlen.\n", anzahl);
    return 0;
    }
  8. FEHLERSUCHE: Ist in dem folgendem Programm irgendetwas falsch?
    #include <stdio.h>

    void funktion_ausgeben( char sternchen );

    int ctr;

    int main(void)
    {
    char sternchen;

    funktion_ausgeben( sternchen );
    return 0;
    }

    void funktion_ausgeben( char sternchen )
    {
    char strich;

    for ( ctr = 0; ctr < 25; ctr++ )
    {
    printf( "%c%c", sternchen, strich );
    }
    }
  9. Was gibt das folgende Programm aus? Führen Sie das Programm nicht aus - versuchen Sie durch Lesen des Codes dahinter zu kommen.
    #include <stdio.h>
    void buchstabe2_ausgeben(void); /* Funktionsprototyp */

    int ctr;
    char buchstabe1 = 'X';
    char buchstabe2 = '=';

    int main(void)
    {
    for( ctr = 0; ctr < 10; ctr++ )
    {
    printf( "%c", buchstabe1 );
    buchstabe2_ausgeben();
    }
    return 0;
    }

    void buchstabe2_ausgeben(void)
    {
    for( ctr = 0; ctr < 2; ctr++ )
    printf( "%c", buchstabe2 );
    }
  10. FEHLERSUCHE: Lässt sich das obige Programm ausführen? Wenn nicht - wo liegt das Problem? Ändern Sie das Programm so, dass es korrekt ist.
12

vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH