10.2. Gültigkeitsbereich von Variablen

Lokale Variablen

Da die Variablen einer Funktion als "privat" gelten, sind sie nur verfügbar, während die betreffende Funktion gerade ausgeführt wird. Solche Variablen heißen deswegen auch "lokale Variablen". Beachten Sie hierzu folgendes Beispiel:


/* scope.c

 * Ungültige Zuweisung zu lokaler Variablen */

#include <stdio.h>

/* Deklaration von Funktionen siehe 10.5. */
void mul(void);

int main(void){

 int iFirst = 24;

   mul();
   return 0;
 }

 /* Funktion mul() */
 void mul(void){

 int iSecond = 12;

 /* iFirst ist hier nicht bekannt und daher keine
  * gültige Variable. Der Compiler meldet deswegen
  * einen Fehler

  */
 iSecond *= iFirst;
 printf("iSecond: %d\n", iSecond);
 }

Das Problem bei scope.c liegt auf der Hand: iFirst ist eine Variable, die in main() definiert wurde und auch nur dort bekannt ist. Der Versuch, in mul() iFirst einen Wert zuzuweisen, quittiert der Compiler mit einer Fehlermeldung wie undefined symbol iFirst in function mul.

Der Geltungsbereich einer Variablen kann sogar auf Teilbereiche einer Funktion eingeschränkt werden. Der Geltungsbereich orientiert sich nämlich nicht an Funktionen, sondern an Anweisungsblöcken. Da der Körper einer Funktion immer in geschweifte Klammern gesetzt werden muß, stellt dieser nur den Spezialfall eines Anweisungsblocks dar. Es spricht aber nichts dagegen, innerhalb von beliebigen Anweisungsblöcken weitere Variablen zu definieren. Dabei ist aber unbedingt zu beachten, daß Variablen-Definitionen nur unmittelbar nach der öffnenden geschweiften Klammer erlaubt sind:


/* scope1.c 
 * lokale Variablen in verschiedenen Geltungs-
 * bereichen können sogar den gleichen Namen
 * haben, ohne daß es zu Nebeneffekten kommt!
*/

#include <stdio.h>

int main(void){

 int iTemp=0, iCount;

 for( iCount = 0; iCount <=3; iCount++){
	/* iTemp wird hier definiert und ist
	 * nur innerhalb des Schleifenkörpers
	 * gültig. Sie überlagert iTemp, die am
	 * Beginn von main() definiert wurde.
	 * Die Variable iTemp des Schleifenkörpers wird
	 * bei jedem Schleifendurchlauf neu er-
	 * zeugt und mit dem Wert 5 initialisiert.
	 */

	int iTemp = 5;

	 iTemp += iCount;
	 printf("iTemp im Schleifenkörper: %d\n",
	 	iTemp);
	 }

 printf("iTemp nach der Schleife: %d\n", iTemp);

 return 0;
 }

Dieses Beispiel zeigt einen Extremfall, was den Geltungsbereich lokaler Variablen betrifft: iTemp wird sowohl am Anfang von main() als auch am Beginn des Schleifenkörpers definiert. Daß es sich trotz der Namensgleichheit um zwei verschiedene Variablen handelt, belegt die Ausgabe von scope1:


iTemp im Schleifenkörper: 5
iTemp im Schleifenkörper: 6
iTemp im Schleifenkörper: 7
iTemp im Schleifenkörper: 8
iTemp nach der Schleife: 0

Kompliziert wird dieses Beispiel dadurch, daß sich die Geltungsbereiche beider Variablen überschneiden. Die erste Variable iTemp wäre normalerweise auch innerhalb der Schleife gültig, weil bei Verschachtelungen die Variablen übergeordneter Blöcke stets verfügbar sind. Durch die Namensgleichheit überlagert aber die innere iTemp die äußere; hieße die zweite Variable nicht ebenfalls iTemp sondern iTemp2, dann wären innerhalb der Schleife sowohl iTemp als auch iTemp2 verfügbar. In der Praxis ist es dringend angeraten, genügend Fantasie bei der Vergabe von Variablennamen aufzubringen und solche verwirrenden Überschneidungen zu vermeiden.

Globale Variablen

Wie der Name schon nahelegt, ist die Gültigkeit globaler Variablen nicht auf einzelne Funktionen beschränkt. Entsprechend können sie durch Anweisungen aller möglichen Funktionen manipuliert werden.

Eine Variable erhält automatisch den Gültigkeitsbereich "global", sobald sie außerhalb einer Funktion definiert wird. Für gewöhnlich werden alle globalen Variablen noch vor main() definiert. Sie können eine globale Variable aber auch erst unmittelbar vor der Funktion definieren, die diese Variable zuerst verwendet. Sie steht dann aber keiner Funktion zur Verfügung, die sich im Quelltext vor der Variablen-Definition befindet. Von einer solchen Verstreuung der Variablen-Definitionen über das ganze Programm ist aber abzuraten, da der Code dadurch unübersichtlich wird.

Existieren eine globale und eine lokale Variable gleichen Namens, dann wird -- wie schon bei geschachtelten Anweisungsblöcken -- die äußere, also die globale, von der inneren (lokalen) Variablen überlagert.


/* global.c */

#include <stdio.h>

/* Funktions-Deklarationen */
void func1(void);
void func2(void);

/* iCount als globale Variable */
int iCount;

int main(void){

/* iCount als lokale Variable */
int iCount;

 for( iCount = 0; iCount >=-3; iCount--)
	/* kein Schleifenkörper */;

 printf("iCount in main(): %d\n", iCount);

 func1();
 return 0;
 }

 void func1( void ){

 printf("Anfangswert von iCount (global): %d\n",
	 iCount);
 while( iCount++ < 5)
	/* kein Schleifenkörper */;
 func2();

 }

 void func2( void ){

  printf("Wert von iCount (global): %d\n", iCount);
  }

Aus global.c ist relativ schnell ersichtlich, wie sich die Gültigkeitsbereiche der lokalen und globalen Variante von iCount verteilen. Erstere ist nur in main() verfügbar, so daß die globale iCount dort vom lokalen Namensvetter überblendet wird. Sowohl func1() wie auch func2() haben ungehinderten Zugriff auf die globale Varialbe iCount, können aber aus verständlichen Gründen die lokale Variable in main() nicht "sehen".

Die Ausgabe dieses Beispiels belegt, daß Veränderungen an einer der beiden Variablen keinen Einfluß auf die andere haben. Sie zeigt außerdem, daß die globale iCount einen Anfangswert von 0 hat, obwohl ihr dieser Wert nirgendwo explizit zugewiesen wurde. Das liegt daran, daß globale Variablen bei ihrer Definition generell mit dem Wert 0 initialisiert werden.

Wie Ihnen wahrscheinlich schon aufgefallen ist, durchkreuzt die Verwendung globaler Variablen das Konzept modularer Programmierung, das wesentlich auf die Abschottung von Daten gegenüber unerwünschten Zugriffen zielt. Globale Variablen sind für jede Funktion dienstbar und öffnen daher allen möglichen unerwünschten Nebeneffekten Tür und Tor. Gerade bei größeren Programmprojekten mit vielen Quelldateien stellen globale Variablen einen Unsicherheitsfaktor dar und sollten daher so weit wie möglich vermieden werden. Wenn Sie schon nicht darauf verzichten können (was wirklich selten der Fall ist), dann sollten Sie mit Hilfe des Schlüsselworts static dafür sorgen, daß eine globale Variable nur den Funktionen bekannt ist, die mit ihr zusammen in der gleichen Quelldatei definiert werden:




#include <stdio.h>

void func1(void);
void func2(void);

/* globale Variable iCount ist nur den
 * Funktionen main(), func1() und func2()
 * bekannt. Werden in anderen Quelldateien
 * weitere Funktionen definiert,
 * können diese wg. "static" nicht auf
 * diese iCount zugreifen.
 */
static int iCount;

int main(void){

.
.

Wenn ein Programm aus mehreren separat compilierten Quelldateien besteht, dann darf eine globale Variable nur einmal definiert werden. Damit der Compiler erkennt, daß Sie nur auf eine bereits anderswo definierte globale Variable Bezug nehmen wollen, müssen Sie diese Variable mit dem Schlüsselwort extern deklarieren. Dies würde dann etwa folgendermaßen aussehen:


extern int iNoOfElements;
/* Variable ist bereits in anderer
 * Quelldatei definiert */

void TuWas(char *pszText);
.
.

Wenn Funktionen globale Variablen nutzen, dann ist es guter Stil, diese dort mit dem Schlüsselwort extern explizit zu deklarieren. Sie können nämlich so die versehentliche Definition einer gleichnamigen lokalen Variablen verhindern:


/* Definition von iCount 
 * als globale Variable */
int iCount;

int main(void){

/* Optionale Deklaration für die globale
 * Variable iCount. Deklarationen reservieren
 * keinen Speicherplatz, sie sind nur Verweise
 * auf bereits existierende Datenobjekte
 */
extern int iCount;
.
.