![]() |
![]() |
C++ - Stack, Speicherallokation |
G.Eichelsdörfer
Staatliche Technikerschule Weilburg |
Ein Programm wird zwecks Ausführung in den Arbeitsspeicher geladen und erhält wenigstens teilweise einen Prozessor, der das Programm ausführt. So wird ein Programm zu einem Prozess. Ein Prozess erhält vor seiner Ausführung zusätzlichen Arbeitsspeicher zugeteilt. Dieser zusätzliche Speicher liegt im Adressraum oberhalb des Programmcodes.
Der Stack (Stapel) beginnt an der obersten Adresse des zugeteilten Speichers und wächst nach unten, dem Programmcode entgegen. Auf dem Stack werden folgende Dinge abgelegt:
Wenn zur Programmlaufzeit für Daten Speicher gebraucht wird, dann kann dieser angefordert bzw. reserviert werden. Dieser Vorgang wird (Speicher-)Allokation genannt. Allokation reserviert für den Anfordernden vom "free store" den angeforderten Speicher, der somit (vorübergehend) dem "free store" entnommen wird. Wie man das Anfordern von Speicher programmiert, wird im Folgenden beschrieben.
Wenn viele Daten zwecks Verarbeitung im Arbeitsspeicher abgelegt werden müssen, dann verwendet man Datenfelder oder einen Container der Standardbibliothek - Container betrachten wir später. Wenn ein solches Datenfeld als lokale Variable angelegt wird, wird dafür der Stack verwendet. Die Kapazität (Größe) eines solchen Datenfeldes muss zur Übersetzungszeit feststehen, weil der Compiler dafür Code erzeugen muss. Liegt diese Kapazität erst zur Laufzeit fest, muss zur Laufzeit dafür Speicher im "free store" reserviert werden. Man nennt diesen Vorgang Speicherallokation - Speicher wird allokiert (manche Autoren schreiben "alloziert"). Ein per Allokation angelegtes Datenfeld nennt man auch ein dynamisches Datenfeld.
Dafür gibt es den Operator new
. Dieser sorgt für die Speicherallokation
und liefert die Anfangsadresse des allokierten Speichers, welche einer geeigneten
Zeigervariablen zuzuweisen ist. Mit Hilfe des Zeigers kann man auf den allokierten Speicher
zugreifen.
Beispiel:
Angenommen wir brauchen Speicherplatz für ein Datenfeld aus 1000 double
-Werten.
double *DP;
DP = new double[1000];
oder kurz
double *DP = new double[1000];
Nun können wir mit Hilfe des Zeigers DP
auf den allokierten Speicher zugreifen -
entweder unter Verwendung eines Index (DP[i]
) oder mit Hilfe der Zeigerarithmetik
(*(DP+i)
).
Beispiel zur Addition aller 1000 Werte im inzwischen gefüllten dynamischen Datenfeld:
double *P, Summe = 0;
for (P=DP;P!=DP+1000;++P)
Summe += *P;
Man kann auch Speicher für eine einzelne Variable allokieren. Dies ist aber erst dann sinnvoll,
wenn ein Zeiger deutlich weniger Speicherplatz benötigt als die dynamisch anzulegende Variable.
Beispiel:
struct Person
{
std::string Name, Vorname;
long geboren;
};
...
Person *PP = new Person;
cin >> (*PP).Name; oder cin >>PP->Name;
// *PP.Name würde PP.Name dereferenzieren (falsch).
Der Pfeiloperator ->
ersetzt bei Zeigern auf Structs die beiden
Operatoren *
und .
sowie die runden Klammern.
Falls nicht mehr genügend Speicherplatz zur Allokation zur Verfügung steht, ist nach dem Standard der gelieferte Zeigerwert undefiniert. Visual C++ liefert in diesem Fall den Nullzeiger NULL. Der Nullzeiger stellt eine ungültige Referenz dar.
Wenn der allokierte Speicher nicht mehr benötigt wird, sollte er wieder freigegeben werden,
um den freien Speicher zu vergrößern. Für die Freigabe dienen die Operatoren
delete
und delete[]
.
Mit dem ersten Operator (ohne Klammern) wird der Speicher einer einzelnen Variablen freigegeben.
Der zweite Operator (mit den Klammern) dient zur Freigabe des Speichers eines Datenfeldes.
In beiden Fällen muss dem Operator der Zeiger auf den Anfang des allokierten Speichers
als Operand folgen.
Zu den obigen Beispielen:
delete PP;
delete[] DP;
Der Vollständigkeit wegen sei hier kurz erwähnt, dass zur Allokation auch die C-Funktionen
malloc()
, calloc()
und zur Freigabe die C-Funktion
free()
zur Verfügung stehen.