Christian Seiler: C++ und der Heap

Beitrag lesen

Hallo,

Ich denke auf den Ersten blick sieht das wie eine ganz normale Klassenmethode.

Naja, ohne Klasse drum herum sieht's eher aus wie eine normale Funktion. ;-)

Dort wird auf dem Heap ein Array mit 1001 Elementen freigeschaufelt und hans zugewiesen.
Dann bekommt hans noch ein bisschen Inhalt und Schließlich wird hans zurückgegeben bzw. seine Adresse.

Ja (mehr oder weniger).

Doch nachdem hans zurückgab existiert er noch immer.

Nein. Der Speicherbereich, auf den "hans" in der Methode gezeigt hat, existiert noch immer, die Variable "hans" existiert nicht mehr.

Was ist denn genau ein Zeiger? Ein Zeiger ist nichts anderes als eine Zahl, der die Programmiersprache eine besondere Bedeutung zuweist. In C/C++ ist es sogar problemlos möglich, Zeiger in Zahlen umzuwandeln und umgekehrt, Beispiel:

#include <iostream>  
#include <string.h>  
  
using namespace std;  
  
int main (int argc, char **argv) {  
   char *zeiger = new char[6];  
   strcpy (zeiger, "hallo");  
   cout << (unsigned long long) (zeiger) << endl;  
   return 0;  
}

Der Code wandelt Dir den Zeiger "zeiger" in eine Zahl um. Was ist nun diese Zahl? Der Ort der Zahl im Speicher des Programms.

Was macht nun new in C++ bzw. malloc in C? Sie reservieren soundsoviele Bytes Speicher im Heap und geben die Adresse dieses Bereichs zurück.

Die Variable des Zeigers (in Deinem Fall "hans") ist für den Compiler selbst vergleichbar mit einer Integer-Variable. Wenn Du nun also den Zeiger zurückgibst:

Seine Variable wird schon lange nicht mehr vom Programm benötigt.
Er existiert noch immer, [...] Oder etwa doch nicht?

Stell Dir vor, Du hättest folgenden Code:

int doppelte (int zahl) {  
  int ergebnis = zahl * 2;  
  return ergebnis;  
}

Hier weist Du der Variable "ergebnis" eine Zahl zu und gibst diese zurück. Die Variable "ergebnis" existiert danach nicht mehr, der WERT, den sie hatte, jedoch weiterhin.

Das gleiche passiert, wenn Du einen Zeiger zurückgibst: Die Zahl, d.h. die Adresse des Speicherbereichs, wird ganz analog zu einer int-Variable zurückgewiesen.

Der Speicherbereich selbst ist auf dem Heap angelegt worden, also gibt es keinen automatisierten Mechanismus, den Speicherbereich wieder loszuwerden, daher bleibt der auch NACH Ausführung der Funktion erhalten.

Wenn hans auf dem Stack wäre würde er doch wieder freigegeben werden?

Ja. Das erreichst Du z.B. so:

char hans[length];

Wenn Du den jetzt zurückgibst, wird Dein Programm gehörig auf die Schnauze fliegen, weil Du einen Zeiger auf einen Speicherbereich zurückgibst, der ungültig ist.

Aber um den Stack muss sich der Programmierer kümmern, also ich.

Du meinst den Heap, oder? Um den Stack kümmert sich in C der Compiler von selbst, d.h. wenn Du eine Funktion aufrufst, passiert dies oder jenes. Und in Assembler musst Du Dich um ALLES selbst kümmern. ;-)

Aber auf dem Stack kann ich ihn nicht werfen da die Größe wahnsinnig Variabel ist von 1 Zeichen bis hin zu 10000.

Ich verstehe Dein konkretes Problem nicht?

Oder soll ich die Funktion alloca() verwenden?

Nein! Das ist genauso, als ob Du obiges char hans[length]; machen würdest! Wenn Du dann einen Zeiger darauf zurückgibst, ist der nicht mehr gültig nach Beendigung der Funktion! Ferner: alloca() steht nicht auf jedem System zur Verfügung und in der Dokumentation derselben Funktion wird explizit davor gewarnt, die einzusetzen, außer man weiß genau, was man tut. Ich würde behaupten, dass man bei normalen Programmen NIE über Notwendigkeit für alloca() stolpert.

Aber soviel ich weis ist die ein wenig langsamer wie new?

Ob alloca() langsamer oder schneller als new ist, weiß ich nicht. Aber alloca() würde ich an Deiner Stelle nicht verwenden. Der EINZIGE sinnvolle Anwendungszweck von alloca() ist, einen Speicherbereich zu reservieren, der DEFINITIV nur für die Laufzeit der Funktion gelten soll und nie länger. Und der einzige Grund, der mir einfiele, warum man nicht einfach trotzdem den Heap nehmen kann und vor dem Funktionsende einfach free / delete machen kann, ist wenn man in C innerhalb der Funktion eine weitere Funktion aufruft, die per longjmp() auf eine per setjmp() festgelegte Stelle springt, die in einer aufrufenden Funktion liegt und man damit keine Kontrolle über das Ende der Funktion hat. Allerdings: setjmp()/longjmp() haben nur SEHR WENIGE sinnvolle Anwendungsmöglichkeiten (ich habe die noch nie gebraucht, obwohl ich C schon EINIGE Zeit lang mache) und mich würde es schon arg wundern, wenn Du die tatsächlich sinnvoll (!) in Deinem Code einsetzen könntest - zumal ich keine Ahnung habe, wie sich longjmp() auf C++ auswirkt (es wurde ja für C konzipiert).

Wenn dem allem nicht so ist und hans doch freigegeben wird, ist mein Posting nicht mehr weiter relevant und somit als erledigt anzusehen.

Du musst hier klar unterscheiden: Zwischen der Variable, die die Adresse des Speicherbereichs enthält und dem Speicherbereich selbst. Die Variable, die die Adresse enthält, ist nicht mehr da nach dem Ende der Funktion, der Speicherbereich selbst bleibt erhalten, falls der Speicherbereich auf dem Heap alloziert wurde und wird "zerstört", falls er auf dem Stack alloziert wurde. Wenn Du den NACH dem Ende der Funktion weiterverarbeite willst, MUSST Du ihn daher auf dem Heap allozieren - und Dich dann manuell um die Aufräumarbeiten kümmern, sobald Du ihn nicht mehr brauchst.

Eine Möglichkeit die mir einfallen würde, wäre hans nach dem Funktionsaufruf wenn sein Inhalt weiterverarbeitet wurde freizugeben.

Beispiel:
[...]

Das Beispiel ist richtig: So müsstest Du das machen.

Viele Grüße,
Christian