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:
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.
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.
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.
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 Funktionmain()
. 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 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.
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.
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.
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.
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 */
}
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.
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.
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.
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.
Merken Sie sich, dass die Funktion | Deklarieren Sie keine statischen Variablen
in |
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.
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:
register
.
main()
ist dies nicht nötig).
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.
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.
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.
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.
int
zwischen
den Aufrufen merken soll, wie muss dann die Variable deklariert werden?
extern
?
static
?
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?
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?
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);
}
}
}
/*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;
}
#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 );
}
}
#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 );
}
In Funktion
wert_ausgeben
:
x
ist nicht deklariert (erste Verwendung in dieser Funktion).
(Auf jeden nicht deklarierten Identifizierer wird für jede Funktion, in der er erscheint, nur einmal hingewiesen.)