Die Computerseite von Eckart Winkler
Statische Variablen in C

 

Sie wissen, daß eine in einer Funktion definierte Variable eine lokale Variable ist, deren Gültigkeitsbereich sich lediglich auf diese Funktion erstreckt. Ihr Wert ist zunächst undefiniert, bis ihr explizit einer zugewiesen wird. Sehen Sie sich einmal die folgende Funktionsdefinition an:

long test(long n)
{
  long x;
  if (n>0) x=n;
  return x;
}

Der Funktion test wird ein long-Parameter übergeben. Ist er größer als 0, wird er der lokalen Variablen x zugewiesen. Sodann wird der Wert von x zurückgegeben. Was das für einen Sinn haben soll, sei einmal zurückgestellt. Sicher sehen Sie aber schon, welches Problem sich hier ergibt: Ist der Parameter n nälich kleiner oder gleich 0, erhält x gar keinen Wert und ist demzufolge undefiniert.

Bei den Aufrufen

printf("Erster Aufruf: %ld\n", test(8));
printf("Erster Aufruf: %ld\n", test(-1));

wird im ersten Fall der Wert 8 ausgegeben, im zweiten Fall aber ein undefinierter, nicht vorhersagbarer Wert.


Definition statischer Variabler

In C gibt es aber auch die Möglichkeit, lokale Variablen einer Funktion als statisch zu vereinbaren. Das bedeutet, daß sie nach Abgabe der Kontrolle an den aufrufenden Programmteil ihre Werte behalten. Beim nächsten Aufruf stehen sie dann wieder zur Verfügung. Damit sind z.B. Funktionen möglich, die bei jedem Aufruf ein anderes Ergebnis liefern, selbst wenn die Argumente identisch sind.

Wie wird das gemacht? Ganz einfach, indem der üblichen Definition einer Variablen das Wort static vorangestellt wird. Die Anweisung static long x vereinbart also eine statische long-Variable x. Mit dieser kleinen Änderung sieht unsere Funktion von eben wie folgt aus:

long test(long n)
{
  static long x;
  if (n>0) x=n;
  return x;
}

Was ist denn damit erreicht? Nun, wenn als Parameter n ein positiver Wert übergeben wird, funktioniert alles wie bisher. Wird aber ein positiver Wert oder 0 übergeben, hat die Variable x den Wert, den sie beim letzten Aufruf hatte.

Das heißt also, bei den Aufrufen

printf("Erster Aufruf: %ld\n", test(8));
printf("Erster Aufruf: %ld\n", test(-1));


ergibt sich nun ein anderes Bild. Im ersten Fall wird wie bisher der Wert 8 ausgegeben, im zweiten Fall auch. Durch die Definition von x als statische Variable merkt sich die Funktion test den Wert von x.


Initialisierung statischer Variabler

So weit, so gut. Was passiert aber, wenn die Aufrufe der Funktion anders herum erfolgen. Also etwa so:

printf("Erster Aufruf: %ld\n", test(-1));
printf("Erster Aufruf: %ld\n", test(8));


Es wird beim ersten Aufruf ein undefinierter Wert ausgegeben. Ganz klar: Die Variable x hat ja noch nie einen definierten Wert erhalten, den sie sich hatte merken können. Erst beim zweiten Aufruf geschieht das, und es wird der Wert 8 ausgegeben. Was also noch fehlt, ist eine ordentliche Initialisierung. Etwa wie folgt:

long test(long n)
{
  static long x=12;
  if (n>0) x=n;
  return x;
}

Nun erhält die Variable x beim ersten Aufruf den Wert 12. Im Gegensatz zur Initialisierung einer normalen lokalen Variablen wird diese Initialisierung aber wirklich nur beim ersten Aufruf durchgeführt wird.

Das heißt, die Abfolge

printf("Erster Aufruf: %ld\n", test(-1));
printf("Erster Aufruf: %ld\n", test(8));
printf("Erster Aufruf: %ld\n", test(-1));

gibt nacheinander die Werte 12, 8 und noch einmal 8 aus. Beim ersten Aufruf durch die Initialisierung mit 12, beim zweiten durch den positiven Parameter 8, beim dritten durch das "Merken" des letzten Wertes.


Ein Zufallszahlengenerator

So weit war es nun ein bißchen theoretisch. Jetzt stellt sich die Frage, wer überhaupt Interesse an dieser Fähigkeit hat, und welche Anwendungen es gibt. Das Standardbeispiel ist der Zufallszahlengenerator, und den wollen wir uns nun einmal ansehen.

Bei einem Zufallszahlengenerator im üblichen Sinn handelt es sich um eine Funktion, die eine Folge von Zahlen liefert. Die Elemente dieser Folge sollen natürlich verschieden sein, das legt die Benutzung von statischen Variablen nahe. Zudem sollen die Zahlen gleichverteilt in einem vorgegebenen Intervall liegen. Ist dieses Intervall beispielsweise der Bereich von 1 bis 30000, so soll jede dieser Zahlen mit gleicher Häufigkeit in der Folge auftauchen. Diese Frage ist aber eher mathematischer Natur und soll hier nicht interessieren.

Es ist nicht ohne weiteres möglich, "echte" Zufallszahlen zu erzeugen. Aus diesem Grunde behilft man sich meist mit einer Folge, für deren Definition es zwei Möglichkeiten gibt. Entweder die Werte x[i] entstammen einer Funktion x[i]=f(i), die möglichst kompliziert gewählt werden sollte. Oder Sie definieren einen Anfangswert x[1] und erklären die weiteren Werte der Folge rekursiv durch Anwendung einer Funktion auf den vorherigen Wert, also x[i]=f(x[i-1]). Die Elemente der resultierenden Folge nennt man "Pseudo-Zufallszahlen".

Bei unserer Realisierung haben wir uns für die zweite Möglichkeit entschieden:

int main()
{
  long i, s;
  scanf("%ld",&s);
  printf("%ld\n",random(s));
  for ( i=0; i<100;i ++ )
    printf("%ld\n",random(0));
}

long random(long n)
{
  static long x=307;
  if (n>0) x=n;
    else x=(x*x)%32003-1;
  return x;
}

Betrachten Sie die Funktion random. Als Anfangswert wird gesetzt x[1]=307, der Wert x[i] wird durch x[i]=(x[i-1]*x[i-1])%32003-1 erhalten, % ist der mod-Operator. Indizes sind nicht zu berücksichtigen. Da Sie nämlich immer nur auf den letzten Wert x[i-1] zurückgreifen, kommen Sie mit einer einzigen Variablen x aus. Alle vorherigen Werte werden also gewissermaßen "vergessen". Die Formel lautet somit einfacher: x=(x*x)%32003-1.

Wie bei professionellen Generatoren üblich, erhält der Benutzer die Möglichkeit, auf die Folge selbst einzuwirken, indem nämlich der Anfangswert von außen gesetzt werden kann. Dazu dient der Parameter n. Ist er größer als 0, wird x=n gesetzt und mit diesem Wert fortgefahren. Ansonsten bleibt der alte Wert von x erhalten.

Was geschieht also beim Aufruf von random? Nun, handelt es sich um den ersten Aufruf, wird zuallererst x=307 gesetzt. Dann erfolgt die Abfrage n>0. Ist dies erfüllt, wird x=n gesetzt, im anderen Fall erfolgt keine Aktion. Auf jeden Fall wird nun der Ausdruck (x*x)%32003-1 ausgewertet, das Resultat an x zugewiesen. x selbst wird zurückgegeben. Auf diese Art laufen auch alle weiteren Aufrufe von random ab. Es besteht jedoch der winzige Unterschied, daß die Zuweisung x=307 nun nicht mehr erfolgt.

Im Hauptprogramm wird einfach ein Anfangswert abgefragt, sodann die ersten hundert Elemente der daraus entstehenden Folge auf dem Bildschirm ausgegeben.


Statische globale Variablen

Nachdem Ihnen nun die prinzipielle Anwendung statischer Variabler klar ist, sollte erwähnt werden, daß das Wort static auch in Zusammenhang mit externen Variablen benutzt werden kann. Dabei sind solche Variablen gemeint, die außerhalb jeder Funktion definiert sind, also als global zu betrachten sind. Setzt man diesen das Wort static voran, schränkt man deren Gültigkeit auf die Quelldatei ein, in der diese Definition steht. In diesem Artikel interessieren uns aber nur lokale statische Variablen, wie sie bei unserem Zufallszahlengenerator auftraten.


Globale Variablen

Fragen wir nun: Was wäre, wenn es in C keine statischen Variablen gäbe? Könnten wir einen Zufallszahlengenerator auch ohne diese programmieren? Selbstverständlich lautet die Antwort: ja. Denn lokale statische Variablen sind nichts anderes als globale Variablen, deren Gültigkeitsbereich auf eine einzige Funktion beschränkt wurde. Im Gegensatz zu "normalen" lokalen Variablen ist jedoch die Lebensdauer mit der Laufzeit des gesamten Programms identisch.

Eine Simulation von statischen Variablen ist also durch globale Variablen prinzipiell möglich. Dann muß jedoch verlangt werden, daß diese von keinem anderen Unterprogramm verändert werden dürfen. Dies ist zweifellos schwer zu kontrollieren, besonders wenn man mit Bibliotheken arbeitet. Zudem wäre es nicht möglich, derartige Unterprogramme als unveränderliche Bibliotheksprogramme zu schreiben. Denn je nach Zusammensetzung des Gesamtprogramms müßten die Namen der betreffenden globalen Variablen geändert werden.


Zusammenfassung

Fassen wir also zusammen: Statische lokale Variablen finden Anwendung bei der Programmierung von Zufallszahlengeneratoren und Betriebssystemen. In beiden Fällen können sie globale Variablen ersetzen. Das Hauptprogramm wird somit von Verwaltungsaufwand entlastet. Zudem wird es übersichtlicher, weil nicht eine große Zahl globaler Variabler zur Speicherung des Zustandes der Unterprogramme vorhanden sein müssen.

 

Übersicht Programmiersprache C Index