C-Programmierung: static & Co.
Aus Wikibooks
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.
[Bearbeiten] static
Das Schlüsselwort static hat in C eine Doppelbedeutung. Im Kontext einer Variablendeklaration sagt dieses Schlüsselwort, dass diese Variable auf einer festen Speicheradresse gespeichert wird. Daraus ergibt sich die Möglichkeit, dass eine Funktion, die mit static-Variablen arbeitet, beim nächsten Durchlauf die Informationen erneut nutzt, die in der Variablen gespeichert wurden wie in einem Gedächtnis. Siehe dazu folgenden Code einer fiktiven Login-Funktion :
-
#include <stdio.h> -
#include <stdlib.h> -
-
int login(const char user[], const char passwort[]) {
-
static int versuch = 0; /* erzeugen einer static-Variablen mit Anfangswert 0 */
-
-
if ( versuch < 3 ) {
-
if ( checkuser(user) != checkpass(passwort) ) { -
versuch++; -
} else { -
versuch=0; -
return EXIT_SUCCESS; -
} -
} -
return EXIT_FAILURE;
-
}
Die in Zeile 5 erzeugte Variable zählt die Anzahl der Versuche und gibt nach 3 Fehlversuchen immer einen Fehler aus, auch wenn der Benutzer danach das richtige Passwort hätte. Wenn vor den 3 Versuchen das richtige Passwort gewählt wurde, wird der Zähler zurück gesetzt und man hat 3 neue Versuche. (Achtung! Dies ist nur ein Lehrbeispiel. Bitte niemals so einen Login realisieren, da diese Funktion z.B. nicht mit mehr als einem Benutzer arbeiten kann).
In der Zeile 5 wird die Variable versuch mit dem Wert 0 initialisiert. Bei einer Variablen ohne das zusätzliche Attribut static hätte dies die Konsequenz, dass die Variable bei jedem Aufruf der Funktion login erneut initialisiert würde. Im obigen Beispiel könnte die Variable versuch damit niemals den Wert 3 erreichen. Das Programm wäre fehlerhaft. Statische Variable werden hingegen 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.
Auch vor Funktionen kann das Schlüsselwort static stehen. Das bedeutet, dass die Funktion nur in der Datei, in der sie steht, genutzt werden kann.
-
static int checklogin(const char user[], const char passwort[]) {
-
if( strcmp(user, "root") == 0 ) {
-
if( strcmp(passwort, "default") == 0 ) { -
return 1; -
} -
} -
return 0;
-
}
Bei diesem Quelltext wäre die Funktion checklogin nur in der Datei sichtbar, in der sie definiert wurde.
[Bearbeiten] volatile
Der Operator sagt dem Compiler, dass der Inhalt einer Variablen sich außerhalb des Programmkontextes ä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;
-
long 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;
-
long count=0;
-
-
while (keyPressed != 'x') {
-
count++; -
}
Das Keyword volatile sollte sparsam verwendet werden, da es dem Compiler jegliches Optimieren verbietet.
[Bearbeiten] register
Dieses Schlüsselwort ist eine Art Optimierungsanweisung 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. Normalerweise werden Variable auf den Stapel (engl. stack) des Prozessors gelegt. Variable in Registern können sehr schnell gelesen und geschrieben werden, und der Compiler kann deshalb sehr effizienten Code generieren. Dies kann zum Beispiel bei Schleifenzählern und dergleichen sinnvoll sein.
Heutige Compiler sind meistens so intelligent, dass das Schlüsselwort register getrost weggelassen werden kann. In der Regel ist es viel besser, die Optimierung des Codes ganz dem Compiler zu überlassen.
Es sollte weiterhin beachtet werden, dass das Schlüsselwort register vom Compiler ignoriert werden kann. Er ist nicht gezwungen, eine so gekennzeichnete Variable in ein Prozessor-Register abzubilden.