PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Paar Fragen zu Stack und Heap


pippo
2006-11-09, 10:32:35
Ich lern jetz grad auf die 2. Informatikprüfung und so ganz hab ich das noch nicht kapiert.

1. Geh ich richtig davon aus, dass der Stack alle Variablen ... enthält, die zu Beginn des Programms schon feststehen? In den Heap kommen dann alle mit new erzeugten Variablen ... ?

2. Sowohl Stack und Heap befinden sich im Arbeitsspeicher und Teile davon werden dann je nach Bedarf in den Cache des Prozessors geladen?

3. Ich les nur immer, dass der Stack begrenzt ist, nicht aber warum. Ist das von Windows bedingt oder physikalisch?

4. Folgendes Codebeispiel aus C++ versteh ich nicht:

...
class EineKlasse
{
public:
EineKlasse(){EineVariable = new int(2)};
~EineKlasse(){delete EineVariable};
int LeseVariable() const {return *EineVariable}

private:
int * EineVariable;
};

int main () {
EineKlasse *Test = new EineKlasse;
...
delete Test;
return 0;
}

Was ich jetzt daran nicht versteh: Das Objekt "Test" liegt ja bereits im Heap und somit doch auch die Variable "EineVariable", oder nicht? Warum wird dann diese "EineVariable" nochmal mit new im Heap erzeugt???


Kann sein, dass ich vor nem Jahr nen Teil schonmal gefragt hab, habs aber nimma gefunden ;)

Xmas
2006-11-09, 12:49:33
Ich lern jetz grad auf die 2. Informatikprüfung und so ganz hab ich das noch nicht kapiert.

1. Geh ich richtig davon aus, dass der Stack alle Variablen ... enthält, die zu Beginn des Programms schon feststehen? In den Heap kommen dann alle mit new erzeugten Variablen ... ?

2. Sowohl Stack und Heap befinden sich im Arbeitsspeicher und Teile davon werden dann je nach Bedarf in den Cache des Prozessors geladen?

3. Ich les nur immer, dass der Stack begrenzt ist, nicht aber warum. Ist das von Windows bedingt oder physikalisch?
Vereinfacht gesagt beinhaltet eine ausführbare Datei folgende Daten:
- Größe und Inhalt des Datensegments (Konstanten, Literale, globale und static-Variablen)
- Größe und Inhalt des Codesegments (sämtlicher ausführbarer Code)
- Größe des Stacks
Wenn du solch ein Programm ausführen willst, reserviert das Betriebssystem erst einmal drei Speicherbereiche entsprechender Größe und kopiert Code und Daten aus der Datei in die entsprechenden Bereiche. Dann wird der Prozess gestartet. Dabei wird ein wenig vom Linker hinzugefügter Initialisierungscode ausgeführt welcher dann nach kurzer Zeit die Kontrolle an deine main-Funktion übergibt.

Auf dieser Basis jetzt zu deinen Fragen:
1. Was meinst du mit "zu Beginn des Programms schon feststehen"? Alle Variablen/Daten deren Lebenszeit der Programmlaufzeit entspricht, liegen im Datensegment. Dazu gehören globale Variablen, static-Variablen und Konstanten/Literale (manche Integer-Literale werden auch direkt in den Code integriert).
Der Heap wird für alle dynamischen Allokationen verwendet, also new und malloc. Auf den Stack kommen alle Auto-Variablen, d.h. alle Variablen, die innerhalb einer Funktion/Methode deklariert werden und nicht static oder const sind. Außerdem werden Funktionsparameter teilweise per Stack übergeben, teilweise aber auch über CPU-Register, je nach Calling Convention.

2. Ja, wobei der Cache transparent ist.

3. Irgendwo liegt natürlich auch eine physische Limitierung des Speichers, aber der Stack ist im Prinzip nur deswegen limitiert weil es seine Verwaltung einfacher macht. Theoretisch könnte man einen Stack einführen, der sich dynamisch anpasst. Das ist aber abhängig vom Betriebssystem.

4. Folgendes Codebeispiel aus C++ versteh ich nicht:

...
class EineKlasse
{
public:
EineKlasse(){EineVariable = new int(2)};
~EineKlasse(){delete EineVariable};
int LeseVariable() const {return *EineVariable}

private:
int * EineVariable;
};

int main () {
EineKlasse *Test = new EineKlasse;
...
delete Test;
return 0;
}

Was ich jetzt daran nicht versteh: Das Objekt "Test" liegt ja bereits im Heap und somit doch auch die Variable "EineVariable", oder nicht? Warum wird dann diese "EineVariable" nochmal mit new im Heap erzeugt???
Test ist nur ein Pointer, kein Objekt vom Typ EineKlasse. Genauso ist EineVaraible nur ein Pointer. Ein Pointer ist eine Variable die eine Zahl — eine Speicheradresse — beinhaltet. Erst new legt ein Objekt vom Typ EineKlasse an und gibt die Adresse des Objekts zurück, welche dann im Pointer Test gespeichert wird.

pippo
2006-11-09, 13:01:34
K, dann schonmal danke soweit. Was 4. betrifft, so hab ich das schon so gemeint, wie du es gesagt hast. Habs nur falsch geschrieben, ja. Dann schreib ichs mal anders:

Test ist also ein Pointer auf ein Objekt im Heap. Welchen Vorteil hab ich also, wenn dieses Objekt nochmal eine Variable mit "new" anlegt und einen Zeiger darauf richtet? Da wir ja eh schon im Heap sind, könnte man sich das ganze doch sparen, oder nicht? Oder braucht man sowas für irgendwas?

Gast
2006-11-09, 13:53:18
Test ist also ein Pointer auf ein Objekt im Heap.

Erst "nachdem" du mit new Speicher auf dem Heap allokiert hast und die int Datenstruktur initialisiert wurde.

Gast
2006-11-10, 17:00:51
K, dann schonmal danke soweit. Was 4. betrifft, so hab ich das schon so gemeint, wie du es gesagt hast. Habs nur falsch geschrieben, ja. Dann schreib ichs mal anders:

Test ist also ein Pointer auf ein Objekt im Heap. Welchen Vorteil hab ich also, wenn dieses Objekt nochmal eine Variable mit "new" anlegt und einen Zeiger darauf richtet? Da wir ja eh schon im Heap sind, könnte man sich das ganze doch sparen, oder nicht? deine Frage scheint daher zu rühren, daß du ein bißchen zu viel Gewicht auf Stack vs. Heap legst.
Für die Frage, ob die dynamische Erzeugung eines Objekts bzw. einer Variablen von Vorteil ist oder nicht, ist unerheblich, ob das Objekt oder der Pointer darauf im Stack oder Heap liegen.
Stack und Heap sind zwei Varianten des internen Speichermanagements, die für den Hochsprachenprogrammier eigentlich bedeutungslos sind, sondern lediglich "nice to have"-Informationen darstellen.

Die Vor- und Nachteile der dynamischen Objekterzeugung sind in deinem Beispiel auch überhaupt nicht zu ersehen:
Der Konstruktor von EineKlasse erzeugt eine int-Variable dynamisch, der Destruktor löscht sie wieder - ebenso gut hättest du die Variable als Member mit automatischer Erzeugung implementieren können, der Effekt wäre derselbe.
Selbiges gilt für die Erzeugung der Instanz von EineKlasse: am Anfang von main() wird sie erzeugt, am Ende vernichtet, genauso als hättest du EineKlasse in main automatisch instantiiert.

Von Vorteil ist dynamische Erzeugung dann, wenn das erzeugte Objekt eine andere Lebensdauer haben soll als die Funktion bzw. das übergeordnete Objekt, von der/dem sie erzeugt wird.
Wenn z.B. in EineKlasse das delete EineVariable nicht im Destruktor gemacht würde, sondern in irgendeiner Methode, die während der Lebensdauer der EineKlasse-Instanz aufgerufen wird, und das delete Test nicht erst am Ende von main() käme, sondern irgendwo mittendrin, dann wären beide Konstruktionen tatsächlich von Vorteil.
Und der Vorteil wäre, daß man so für die EineKlasse-Instanz eine andere Lebensdauer erreicht als für main(), und für die int-Instanz eine andere Lebensdauer als für die EineKlasse-Instanz.

Xmas
2006-11-10, 19:53:58
Stack und Heap sind zwei Varianten des internen Speichermanagements, die für den Hochsprachenprogrammier eigentlich bedeutungslos sind, sondern lediglich "nice to have"-Informationen darstellen.
Gerade gestern erst musste ich erfahren dass es doch nicht so ganz bedeutungslos ist, wenn das Anlegen eines Objekts als Auto-Variable einen Stack Overflow Error auslöst. Es stellte sich dann heraus dass die betreffende Instanz über 2 MiB beanspruchte...