Arrays auf dem Heap

Content

Variablen können auch auf dem Heap angelegt werden. Mit dem Operator new wird Speicher belegt, mit delete wieder freigegeben. Die Operatoren unterscheiden dabei zwischen Arrays und anderen Objekten. Bei Arrays müssen immer die eckigen Klammern mit angegeben werden, auch bei delete.

Der new-Operator liefert ein Zeiger auf den Elementtyp zurück. Zeigertypen sind in C++ explizit. Beim declarator wird ein Zeigertyp durch den Präfix-Operator * spezifiziert (der auch zum Dereferenzieren verwendet wird).

Zunächst ein triviales Beispiel ohne Arrays:

#include <iostream>

int main() {
   int *ip = new int(42); // Speicherbelegung und Initialisierung
   std::cout << "answer: " << *ip << std::endl;
   delete ip; // Freigabe des Speichers
}

Wie sich hier sehen lässt, ist beim Belegen des Speichers mit new auch gleichzeitig eine Initialisierung möglich. Nach der Nutzung wird die Speicherfläche wieder freigegeben. Nach dem delete darf diese nicht mehr benutzt werden. (Überprüft wird dies jedoch nicht, kann aber zu katastrophalen Effekten führen.)

Interessanter ist die Nutzung von new für Arrays. Wir erhalten damit die Möglichkeit, Arrays dynamisch zu dimensionieren:

#include <cstdlib>
#include <iostream>

int main() {
   std::size_t size;
   if (std::cin >> size && size > 0) {
      int* array = new int[size];
      for (std::size_t i = 0; i < size; ++i) {
	 array[i] = (i+1)*(i+1);
      }
      for (std::size_t i = 0; i < size; ++i) {
	 std::cout << array[i] << std::endl;
      }
      delete[] array;
   }
}

Für dynamische Array-Größen wird bevorzugt der Datentyp std::size_t verwendet, dieser ist nicht-negativ und für beliebig große Objekte geeignet. Für diesen Datentyp ist #include <cstdlib> notwendig. Mit dem >>-Operator sind Eingaben möglich. Das Resultat von std::cin >> size ist wieder std::cin. Wenn aber ein Stream in einer Bedingung auftaucht, wo bool benötigt wird, dann liefert der Stream automatisch ein bool-Resultat, der wiedergibt, ob zur Zeit noch alles ok sind. Der Fehlerschalter bleibt übrigens bestehen, bis er explizit weggenommen wird. Nachdem die Größe erfolgreich eingelesen wurde und positiv ist, kann das Array auf dem Heap belegt werden. Die Benutzung des Arrays funktioniert danach wie zuvor, da array[i] entsprechend der Adressarithmetik äquivalent zu *(array + i) ist, dazu später mehr. Zuletzt geben wir das Array wieder frei, wobei hier beim delete-Operator die eckigen Klammern nicht vergessen werden dürfen. So sieht dann ein kleiner Testlauf aus:

theon$ echo 12 | ./array-on-heap
1
4
9
16
25
36
49
64
81
100
121
144
theon$

(Das Kommando echo 12 gibt einfach die 12 aus. Mit dem Pipe-Symbol | wird die Standard-Ausgabe des vorangehenden Kommandos zur Standard-Eingabe des Folgekommandos.)

Aufgabe

Passen Sie Ihr Sieb des Eratosthenes so an, dass die Größe des Siebs eingelesen und das entsprechende Array auf dem Heap angelegt wird. Am Ende sollte es wiederum freigegeben werden. Denken Sie daran, welchen anderen Datentyp Sie auch noch anpassen müssen. Falls nicht, wird Ihnen -Wall eine Warnung bescheren.