C++ Kurs

Speicherklassen und Qualifizierer

Die Themen:

C++ kennt folgende Speicherklassen:

 register, extern, static, mutable

Um eine Variable einer bestimmten Speicherklasse zuzuordnen, wird vor dem Datentyp der Variablen die entsprechende Speicherklasse angegeben. Zudem können Variablen, bedingt durch die Stellung ihrer Definition im Programm, implizit einer bestimmten Speicherklasse angehören.

PgmHeader
register int count;
extern char *pText;
static bool first;

Außer Speicherklassen können Variablen zusätzlich noch einen sogenannten Qualifizierer (Qualifier) besitzen. Folgende Qualifizierer stehen zur Verfügung:

const, volatile

Die Angabe eines zusätzlichen Qualifizierers erfolgt vor dem Datentyp der Variablen, aber nach einer eventuell vorhandenen Speicherklasse.

PgmHeader
const int NUMBERELEMENTS = ...;
volatile unsigned long ticks;
extern volatile bool portReady;
AchtungIm alten C++ Standard gab es noch die Speicherklasse auto. Da die Angabe dieser Speicherklasse redundant war, wurde sie nicht in den C++11 Standard übernommen. Vielmehr erhielt das Schlüsselwort auto ab C++11 eine völlig neue Bedeutung (siehe dazu Variablen).

register Speicherklasse

Die Speicherklasse register ist nur für lokale Variablen oder für Funktionsparameter zulässig. Der Compiler versucht Variablen dieser Speicherklasse innerhalb eines Prozessor-Registers abzulegen anstatt im Speicher. Aber wie gesagt, der Compiler versucht nur die Variable in einem Prozessor-Register unterzubringen. Selbst wenn mehrere Dutzend Variablen dieses Typs definiert werden, obwohl der Prozessor z.B. nur 8 Register besitzt, meldet der Compiler beim Übersetzen des Programms keinen Fehler.

PgmHeader
// Funktion mit register Parameter
void PrintIt(register char *pText);

// Nicht erlaubt, da register nur auf lokale Variablen
register short count;

int main()
{
   register int loop;
   ...
}
void PrintIt(register char *pText)
{
   ...
}

Wenn versucht wird, von einer register Variable die Adresse zu bilden, legt der Compiler die Variable automatisch im Speicher ab, da C++ davon ausgeht, dass von Registern keine Adresse gebildet werden kann.

HinweisModerne Compiler optimieren in der Regel so gut, dass Sie sich nicht um die optimale Ablage von Variablen kümmern müssen. Die Speicherklasse register hat in der heutigen Zeit mehr oder weniger historische Bedeutung.

extern Speicherklasse

Variablen oder Funktionen der Speicherklasse extern teilen dem Compiler mit, dass ihre Definition in einem anderen Modul (Quellcode-Datei) erfolgt als im aktuellen. D.h. als externe deklarierte Daten belegen zunächst einmal keinen Speicherplatz, sondern erst bei ihrer Definition in einem anderen Modul.

PgmHeader
// Datei FILE1.CPP

// Funktionsdeklaration

void PrintIt (const char* const pText);
// Definition einer globalen Variable
short counter;
...
// Definition der Funktion
void PrintIt (const char* const pText)
{
   ...
}
PgmHeader
// Datei FILE2.CPP

// Verweis auf Fkt. im Modul FILE1.CPP

extern void PrintIt (const char* const);
// Verweis auf Variable im Modul FILE1.CPP
extern short counter;
...
HinweisVergessen Sie eine als extern deklarierte Variable oder Funktion in einem anderen Modul zu definieren, so erhalten Sie zunächst keinen Übersetzungsfehler. Erst beim Linken (Zusammenbinden) der Module erhalten Sie vom Linker eine entsprechende Fehlermeldung, da er die Definition nicht findet.

Eine etwas andere Wirkung besitzt die Speicherklasse extern bei benannten Konstanten. Wie schon im Kapitel über Konstanten erwähnt, sind benannte Konstanten standardmäßig modulglobal, d.h. nur in der Quellcode-Datei gültig, in der sie definiert sind. Soll eine benannte Konstante so definiert werden, dass sie in verschiedenen Modulen verwendet werden kann, so muss sie sowohl bei ihrer Definition als auch bei den Verweisen darauf als externe Konstante definiert werden. Hierbei ist aber zu beachten, dass die Konstante nur einmal initialisiert werden darf; ansonsten erfolgt beim Linken des Programms eine Fehlermeldung, dass die Konstante mehrfach definiert ist.

PgmHeader
// Datei FILE1.CPP

// Definition der Konstanten
extern const int MAX = 5;
...
PgmHeader
// Datei FILE2.CPP

// Verweis auf benannte Konstante
extern const int MAX;
...
DetailDie Speicherklasse extern wird auch dazu verwendet, um C Funktionen in ein C++ Programm einzubinden. Wie dies geht erfahren Sie, wenn Sie links das Symbol anklicken.

static Speicherklasse

Kommen wir jetzt zur Speicherklasse static. Globale Variablen und Funktionen dieser Speicherklasse sind nur in dem Modul sichtbar und damit gültig, in dem Sie definiert sind. Eine extern-Referenz auf eine Variable oder Funktion dieses Typs führt zu einer Fehlermeldung beim Linken des Programms.

Lokale Variablen der Speicherklasse static behalten ihren letzten Wert auch dann noch bei, wenn ihr Gültigkeitsbereich verlassen wird, d.h. sie werden beim Verlassen ihres Gültigkeitsbereichs nicht gelöscht. Beim nächsten Eintritt in den Gültigkeitsbereich kann dann mit dem zuletzt abgespeicherten Wert weiter gearbeitet werden. Wohl gemerkt, dies betrifft nur die Erhaltung des Wertes, der Gültigkeitsbereich der Variable bleibt weiterhin auf den Block begrenzt, in dem sie definiert ist. Im Beispiel unten wird die Variable count dazu verwendet, die Anzahl der Funktionsaufrufe zu zählen.

Da lokale Variablen aber nicht automatisch initialisiert werden, sollten static Variablen bei ihrer Definition mit einem Startwert versehen werden. Diese Initialisierung wird aber nur ein einziges Mal ausgeführt, nämlich beim Reservieren des Speicherplatzes für die Variable.

PgmHeader
// Funktionsdeklaration
static void PrintIt (const char *pText);
...
// Funktionsdefinition
void PrintIt(const char *pText)
{
   static auto count = 0;
   ...
   count++;
}

mutable Speicherklasse

Die mutable Speicherklasse spielt nur im Zusammenhange mit Klassen eine Rolle und ist nur der Vollständigkeit halber hier erwähnt. Mehr dazu später bei der Einführung von Klassen.

const Qualifizierer

Der const Qualifizierer im Zusammenhang mit Variablen (einfachen Variablen, Zeiger und Felder) definiert den Inhalt der Variable oder des Feldes als konstant. Daraus folgt, dass const Variablen immer initialisiert werden müssen, da eine nachträgliche Änderung des Wertes nicht mehr zulässig ist.

Später im Kurs werden wir uns diesen Qualifizierer nochmals ansehen, und zwar im Zusammenhang mit Klassen.

volatile Qualifizierer

Kommen wir jetzt zum Qualifizierer volatile, der in der Regel nur für Variablen verwendet wird. Eine als volatile definierte Variable kann auch außerhalb des normalen Programmablaufs, und damit auf eine nicht vom Compiler feststellbare Art und Weise, ihren Wert ändern. Ursache für solche asynchronen Zustandsänderungen von Variablen können z.B. ein Betriebssystem, die Hardware (Interrupts) oder ein quasi parallel laufender Thread (Thread, vereinfacht ausgedrückt: ein Programm das parallel zu einem anderen läuft und mit diesem Daten austauschen kann) sein. Eine typische volatile Variable ist z.B. die Systemzeit. Die Systemzeit wird in der Regel nicht durch die Applikation verändert sondern durch das Betriebssystem.

PgmHeader
// Variable welche die Systemzeit enthält
// Wird vom Betriebssystem aktualisiert

volatile unsigned long ticks;
...
// Beliebige Funktion
void DoAnything(...)
{
   // Lokale Variable zum Zwischenspeichern der Systemzeit
   unsigned long var;
   ...
   var = ticks;
   ...
   var = ticks;
}

Da die heutigen Compiler in der Regel sehr gut optimieren, könnte im Beispiel oben ohne volatile die zweite Zuweisung unter gewissen Bedingungen durch den Compiler entfernt werden, da sowohl var als auch ticks zwischen diesen beiden Anweisung augenscheinlich nicht verändert werden. Durch die Definition von ticks als volatile wird dem Compiler mitgeteilt, dass diese Variable auch außerhalb des normalen Programmablaufs (z.B. in einer Interrupt-Routine) verändert werden kann und damit keinerlei Optimierungen bezüglich des zu erwartenden Inhalts von ticks vorgenommen werden dürfen. Bei jedem Lesezugriff auf ticks wird immer der aktuelle Wert aus dem Speicher ausgelesen und jeder Schreibzugriff führt zur sofortigen Ablage des neuen Werts im Speicher.

AchtungDie Definition einer Variable mittels auto entfernt die Qualifizierer const und volatile.
PgmHeader
// volatile Variable definieren
volatile unsigned long ticks;
...
// Datentyp von actTicks ist lediglich unsigned long (ohne volatile)
auto actTicks = ticks;

Zum Schluss noch der Hinweis darauf, dass es auch möglich ist, Memberfunktion als volatile zu kennzeichnen.

Beispiel und ÜbungUnd hier geht's zum Beispiel und zur Übung.