C-Programmierung: static & Co.

Manchmal reichen einfache Variablen, wie sie im vergangenen Kapitel behandelt werden, nicht aus, um ein Problem zu lösen. Deshalb stellt der C-Standard einige Operatoren zur Verfügung, mit denen man das Verhalten einer Variablen weiter präzisieren kann.

staticBearbeiten

Das Schlüsselwort static hat in C eine Doppelbedeutung.
Im Kontext einer Variablendeklaration innerhalb einer Funktion bewirkt dieses Schlüsselwort, dass die Variable auf einer festen Speicheradresse gespeichert wird. Abgesehen vom ersten Aufruf der Funktion, werden die Informationen erneut genutzt, die in der Variablen gespeichert wurden (wie in einem Gedächtnis). Siehe dazu folgendes Codebeispiel:


int next_number()
{
   static int number = 0;   /* erzeugen einer static-Variablen mit Anfangswert 0 */
   return ++number;   /* inkrementiert die Zahl und gibt das Ergebnis zurück */
}

Beim ersten Aufruf wird 1 zurückgegeben, beim zweiten Aufruf 2, beim dritten 3, etc.
Statische Variablen werden nur einmal initialisiert, und zwar vom Compiler. Der Compiler erzeugt eine ausführbare Datei, in der an der Speicherstelle für die statische Variable bereits der Initialisierungswert eingetragen ist.
Ohne static würde number bei jedem Aufruf mit 0 initialisiert auf den Stack gelegt werden und die Funktion würde immer 1 zurückgeben.

Auch vor Funktionen sowie Variablen außerhalb von Funktionen kann das Schlüsselwort static stehen.
Das bewirkt, dass auf die Funktion bzw. Variable nur in der Datei, in der sie steht, zugegriffen werden kann.

static int is_small_letter(char l)
{
   return l >= 'a' && l <= 'z';
}

Bei diesem Quelltext wäre die Funktion is_small_letter nur in der Datei sichtbar, in der sie definiert wurde.

volatileBearbeiten

Der Operator volatile sagt dem Compiler, dass der Inhalt einer Variablen sich außerhalb des normalen Programmflusses ändern kann. Das kann zum Beispiel dann passieren, wenn ein Programm aus einer Interrupt-Service-Routine einen Wert erwartet und dann über diesen Wert einfach pollt (kein schönes Verhalten, aber gut zum Erklären von volatile). Siehe folgendes Beispiel

char keyPressed;
int count=0;

while (keyPressed != 'x') {
   count++;
}

Viele Compiler werden aus der while-Schleife ein while(1) machen, da sich der Wert von keyPressed aus ihrer Sicht nirgendwo ändert. Der Compiler könnte annehmen, dass der Ausdruck keyPressed != 'x' niemals unwahr werden kann. Achtung: Nur selten geben Compiler hier eine Warnung aus. Wenn Sie jetzt aber eine Systemfunktion geschrieben haben, die in die Adresse von keyPressed die jeweilige gedrückte Taste schreibt, kann das oben Geschriebene sinnvoll sein. In diesem Fall müssten Sie vor der Deklaration von keyPressed die Erweiterung volatile schreiben, damit der Compiler von seiner vermeintlichen Optimierung absieht. Siehe richtiges Beispiel:

volatile char keyPressed;
int count=0;

while (keyPressed != 'x') {
   count++;
}

Das Keyword volatile sollte sparsam verwendet werden, da es dem Compiler jegliches Optimieren verbietet.

registerBearbeiten

Dieses Schlüsselwort ist ein Optimierungshinweis an den Compiler. Zweck von register ist es, dem Compiler mitzuteilen, dass man die so gekennzeichnete Variable häufig nutzt und dass es besser wäre, sie direkt in ein Register des Prozessors abzubilden. Ohne Compileroptimierung werden Variablen auf dem Stapel (engl. stack) abgelegt. Register können jedoch wesentlich schneller gelesen und beschrieben werden als der Arbeitsspeicher (oder Prozessor-Cache), der den Stack enthält.

Bei der Verwendung dieses Schlüsselworts sollte man folgendes bedenken:

  • Register haben eine begrenzte Anzahl und Größe (zB. 32 Bit).
  • register Variablen können nicht dereferenziert werden, unabhängig davon, ob der Compiler die Variable tatsächlich nicht auf den Stack legt.
  • Das Schlüsselwort ist ein Hinweis, d.h. der Compiler kann schließlich dennoch die Variable auf den Stack legen.
  • Unter normalen Umständen sollte register nicht verwendet werden, moderne Compiler entscheiden automatisch, ob es effizient ist, ein Register für die Variable zu reservieren. [1].

In der Compiler-Dokumentation kann eingesehen werden, wie der Compiler register oder andere Optimierungen behandelt oder behandeln soll.