allgemeine (Anfänger-)Fragen zu Heap und Stack
-
Hallo,
ich hab da mal ein paar Fragen insbesondere zur Verwaltung meiner (lokalen) Variablen. Also ich weiß bis jetzt, dass dynamisch reservierter Speicher auf dem Heap angelegt wird (angefordert durch malloc und co). Lokale Variablen werden dagegen auf dem Stack angelegt.
Wenn ich jetzt weiß, dass ich bspw. ein int-array benötige das ziemlich groß wird, sollte ich das lieber gleich auf dem Heap reservieren? Oder spielt die Größe gar keine Rolle und benutze ich den Heap wirklich NUR, wenn ich nicht von vornherein weiß wie groß mein Array wird?
Und nochmal so am Rande
Wo liegen denn Heap und Stack? Beide im RAM? Ich wär auch dankbar für ne informative Website zu dem Thema...
Danke schonmal
-
hmm ich hab das eigentlich genau anders herum in erinnerung. Der Stack ist das wo jede Funktion etc nen eigenen hat. Also das was dynamisch gefüllt wird. Der Heap wo etwas global drinne gespeichert wird. Den Stack kannst du dir wie einen großen haufen "leerer" Speicherzellen vorstellen die übereinander im ram liegen. Jedes mal wenn du nun mit malloc dort speicher anforderst werden ein paar zellen genommen und sozusagen als "nicht mehr frei" deklariert. Dort kannst du dann lokale werte reinschreiben. Wenn du nun free machst wird einfach wieder gesagt die Zellen dürfen nun wieder benutzt werden. Wo genau das im ram steht weisst du nicht. Hat einen einfachen grund. Du weisst ja nichtmal wo im ram dein programm steht. Beim start deines Programmes sagt sozusagen dein OS. Ok hier ist etwas speicher frei. Also bekommt dein programm von Adresse 0x200 - 0xFFF (z.B.) Speicherplatz. Und irgendwo dort drinnen befindet sich dann heap und stack, meist wird aber der stack am oberen rand angelegt und nach unten hin aufgebaut. Er würde also bei 0xFFF beginnen aber halt eben umgedreht.
Der Heap ist ein Bereich in dem dann halt deine Konstenten stehen. Also wenn du irgendwo ein static wert hast etc. Gibts irgendwo einen bereich den das programm kennt wo halt wirklich in einer festen adresse dieser Wert steht. Der steht auch noch da drinne bis du dein programm beendest. Nix kann so einfach da dran rütteln. im grunde ist dein Variablenbezeichner damit auch nur irgend eine art "Zeiger" die auf genau diese Adresse zeigt. Nur das die halt konstant ist.
P.S.
es stimmt natürlich alles nur im groben (sofern ich nicht heap und stack komplett vertauscht habe) denn die bereiche können sich auch noch ändern etc. Da müssten man jetzt mit einer MMU bzw auch noch mit Paging oder Segmenting kommen was den rahmen aber sprengen würde. Ist nur um einige leute die sonst gleich mit dem erhobenen Zeigefinger kommen "So ist das aber nicht" zu beruhigen.
-
@Fedaykin:
Doch, irgendwas hast Du da vertauschtAuf dem Stack landet alles, was lokal für Funktionen und Methoden ist, also Argumente, lokale Variablen und einige Implementationsdetails wie Rücksprungadressen. Der Stack "wächst" mit jedem Funktionsaufruf und "schrumpft" bei jedem Rücksprung.
Der Heap verwaltet alles, was dynamisch angefordert werden muss und nicht an die Lebensdauer eines Scope gebunden ist, sprich alles was aus der malloc-Familie kommt bedient den Heap.
Statische und globale Variablen landen dann nochmal woanders, in einigen Implementationen in etwas was Datensegment genannt wird.
-
Vielen Dank Euch beiden für die ausführlichen Antworten!
Was mich ja außerdem interessiert hat war die Frage, ob es Sinn macht, Speicherplatz für ein Array, das sehr groß ist, gleich als Heap zu reservieren. Ich stell mir das irgendwie effizienter vor, wenn ich viel Speicherplatz brauche. Irgendwie hab ich das Gefühl, dass könnte besser sein, wenn der Stack nicht 10km lang wird wegen ein paar riesen Arrays. Oder lieg ich da total falsch? Vielleicht ist es ja auch sowas von egal, wie lang der Stack wird???
-
also große arrays sind generell im heap dann gut aufgehoben, unser c prof hat mal gesagt warum.... ich glaube mal der zugriff war irgendwie schneller oder so. Hatte nen sehr einleuchtenden grund den ich schon wieder vergessen hab
-
hm der grund würd mich jetzt schonmal interessieren. aber zumindest bin ich schonmal beruhigt, dass meine frage nicht ganz sinnlos war. vielleicht hat das ja was mit dem speicherprinzip zu tun? stack is ja lifo, vielleicht wird ja der heap anders verwaltet?
-
hi,
stack:
der 'stack' ist meistens schon im prozessor eingebaut. es gibt ein spezielles register (den sogenannten stack pointer), und spezielle instructions um mit dem stack zu arbeiten. es gibt auch prozessoren, die keinen eingebauten stack-pointer haben (da kann man dann jedes register als stack pointer nehmen), und solche, die zwei stacks haben (einen user-stack und einen systemstack). manche speichern rücksprungadressen von 'call' instructions und interrupts auf dem stack, andere pushen auch gleich das flagregister mit drauf, die, die keinen stack haben, haben auch keine call-instructions bzw. ein paar bytes ram intern, um einige wenige rücksprungadressen zu speichern. manche haben spezielle mechnismen um stack over/underflows zu erkennen und dann 'ne exception auszulösen, usw. usw. usw...
es sind alle varianten möglich (und sie werden auch verwendet). das einzige was überall gleich ist, ist dass ein stack aus benutzersicht immer eine LIFO struktur ist.heap:
heaps werden immer durch software realisiert (zumindest fällt mir kein prozessor ein, der 'ne heapverwaltung in hardware hat). so'n heap braucht man, um einen grossen speicherbereich in kleine stückchen zu unterteilen. heaps basieren meistens auf verketteten listen. es gibt heaps, die bis auf's byte genau speicher allozieren, andere wiederum tun das in festen blöcken (die sind dann schneller, fragmentieren nicht so stark, gehen aber verschwenderischer mit dem speicher um). auf pc-betriebssystemen wie windoofs bekommt z.b. jeder prozess seinen default-heap vom system zugeteilt (er kann aber auch selber eigene heaps aufmachen). bei systemen ohne OS muss man einen festen RAM-bereich wählen, der dann als heap-speicher verwendet wird (wenn man da 'malloc' o.ä. verwenden will)...
-
Würd_gern_C_können schrieb:
Hallo,
Wenn ich jetzt weiß, dass ich bspw. ein int-array benötige das ziemlich groß wird, sollte ich das lieber gleich auf dem Heap reservieren? Oder spielt die Größe gar keine Rolle und benutze ich den Heap wirklich NUR, wenn ich nicht von vornherein weiß wie groß mein Array wird?
Und nochmal so am Rande
Wo liegen denn Heap und Stack? Beide im RAM? Ich wär auch dankbar für ne informative Website zu dem Thema...
Ja Heap und Stack liegen beide im gleichen Adressraum, unter Windows ist das der virtuelle Adressraum für den Prozess und nur benötigte Teile des Programmes werden durch einen Pagingmechanismus in den "physikalischen RAM" geladen. Die ganze Speicherverwaltung ist Sache deines Betriebssystems der Prozessor ansich stellt jedoch "Methoden" zur Unterstützung bereit. Das wäre z.B. auch die Segmentadressierung, damit kann der Speichere in diverse Adressräume zerlegt werden und es können z.B. auch differenziertere "Zugriffsrechte" auf die verschiedenen Segmente gelegt werden.
Der Stack, unter Intelarchitekturen, dient zum Speichern von Funktionsaufrufen, Übergabeparametern, lokalen Variablen, Rücksprungadressen und genau dafür sollte er auch nur verwendet werden und nicht als Datenspeicher für riesige Datenstrukturen. Dass ist auch durch die Größe bedingt die ist oft recht klein gewählt ein paar MB oder sogar nur 1MB, das unterstreicht auch den Verwendungszweck des Stacks. Jedenfalls kann der Stack aber bis 4GB groß sein unter IA-32.
Für dich ist wichtig zu wissen das funktionslokale Variablen auf dem Stack landen, d.h. du rufst eine Funktion auf die Parameter kommen auf den Stack die Rücksprungadresse dann die lokalen Variablen wie Arrays usw. wenn die Funktion jedoch beendet ist und "zurückkehrt" wird der ganze Speicher für den obigen Kram wieder freigegeben und ist somit also weg.
Wenn du dein Array ausserhalb einer Funktion deklarierst hast du nur drei Möglichkeiten entweder steht es in Main() und landet somit auf dem Stack und verschwindet nach beenden des ganzen Programmes oder du legst es als globale Variable an dann landet das Array aber nicht auf dem Stack oder du legst es dynamisch an dann landet es auch nicht auf dem Stack.
Du hast also nicht die Auswahl ob du nun gerne ein Array auf dem Stack anlegen möchtest oder nicht, vorallem nicht wenn du die Daten ausserhalb der Funktion weiterverwenden wollen würdest. Und auch so denke ich, sagt dir schon der gesunde Menschenverstand das wenn du ein Array mit ein paar tausend Werten immer wieder neu auf dem Stack angefordert werden muss nicht sonderlich effizient sein kann.
tt
-
TheTester schrieb:
Und auch so denke ich, sagt dir schon der gesunde Menschenverstand das wenn du ein Array mit ein paar tausend Werten immer wieder neu auf dem Stack angefordert werden muss nicht sonderlich effizient sein kann.
Ja, der gesunde Menschenverstand hat mir sowas in der Art geflüstert, aber er hat mir nicht verraten, warum genau das so ist. Deswegen hab ich hier nochmal nachgefragt. Und ich muss sagen, dass mir Eure Beiträge wirklich weiter geholfen haben. So kompakt und auch relativ unkompliziert habe ich es nirgends im Netz gefunden. Dickes Dankeschön!
-
vista schrieb:
hi,
stack:
der 'stack' ist meistens schon im prozessor eingebaut.Das finde ich unglücklich formuliert. Der physikalische Speicher des Stacks ist bestimmt nicht IM Prozessor eingebaut.
-
this->that schrieb:
vista schrieb:
hi,
stack:
der 'stack' ist meistens schon im prozessor eingebaut.Das finde ich unglücklich formuliert. Der physikalische Speicher des Stacks ist bestimmt nicht IM Prozessor eingebaut.
Ich glaub aber, dass ichs trotzdem verstanden habe.
Ich denke mal dass die meisten Prozessoren den Speicher auf die Art verwalten, dass Sie ein oder mehrere Register als Stack reservieren.
-
Die Prozessoren selber verwalten weder Heap noch Stack - das ist Sache des Betriebssystems.
Und die Register werden auch nicht ALS Stack benutzt, sondern zur Referenzierung des Stacks (drum heißt das Stack-Register auch SP - Stack Pointer).
-
Ok, also steht in dem Stack-Register nur die Adresse des Stacks. Hab ich das richtig verstanden, dass die Größe vom Stack vordefiniert ist (vom OS)?
TheTester schrieb:
Dass ist auch durch die Größe bedingt die ist oft recht klein gewählt ein paar MB oder sogar nur 1MB, das unterstreicht auch den Verwendungszweck des Stacks. Jedenfalls kann der Stack aber bis 4GB groß sein unter IA-32.
ttDann liegt im Stack-Register auch noch die "End-Adresse" vom Stack? Und wenn ich das Ende vom Stack irgendwann mal überschreiten sollte, ist das dann auch ein Buffer-Overflow? Und was passiert dann?
-
this->that schrieb:
vista schrieb:
hi,
stack:
der 'stack' ist meistens schon im prozessor eingebaut.Das finde ich unglücklich formuliert. Der physikalische Speicher des Stacks ist bestimmt nicht IM Prozessor eingebaut.
ja, stimmt, ist blöd formuliert. der stack-speicher ist natürlich nicht im prozessor, sondern nur die verwaltungsfunktionen, also stack-pointer, PUSH/POP/CALL/RET-befehle usw.
heap und stack sind beides RAM bereiche, wobei der eine eben automatisch durch den prozessor selber verwaltet wird, während man für den anderen eine softwarekomponente (manchmal heap-manager genannt) braucht...
Würd_gern_C_können schrieb:
Dann liegt im Stack-Register auch noch die "End-Adresse" vom Stack? Und wenn ich das Ende vom Stack irgendwann mal überschreiten sollte, ist das dann auch ein Buffer-Overflow? Und was passiert dann?
der stack-pointer ist einfach nur ein zeiger. wenn sein wertebereich um 1 überschritten wird, steht der wieder auf 0. und umgekehrt, ist er 0 und man führt einen POP- oder RET-befehl aus, dann dann hat er plötzlich einen irre hohen wert. beides ist natürlich ein fehler, der in einem 'richtigen' programm nicht passieren darf...
-
Würd_gern_C_können schrieb:
Ok, also steht in dem Stack-Register nur die Adresse des Stacks. Hab ich das richtig verstanden, dass die Größe vom Stack vordefiniert ist (vom OS)?
Nein ist sie nicht, die kannst du "frei" wählen und so hoch setzen wie dein Prozessor adressieren kann.
TheTester schrieb:
Dass ist auch durch die Größe bedingt die ist oft recht klein gewählt ein paar MB oder sogar nur 1MB, das unterstreicht auch den Verwendungszweck des Stacks. Jedenfalls kann der Stack aber bis 4GB groß sein unter IA-32.
ttWürd_gern_C_können schrieb:
Dann liegt im Stack-Register auch noch die "End-Adresse" vom Stack? Und wenn ich das Ende vom Stack irgendwann mal überschreiten sollte, ist das dann auch ein Buffer-Overflow? Und was passiert dann?
Nein, die liegen für gewöhnlich in den Globalen und Lokalen Descriptor Tables. Und wenn es die End-Adresse erreicht sollte eine Exception vom Prozessor auftreten und dein Programm wird für gewöhnlich abstürzen. Beim Stack nennt man das dann Stackoverflow.
-
Würd_gern_C_können schrieb:
Dann liegt im Stack-Register auch noch die "End-Adresse" vom Stack? Und wenn ich das Ende vom Stack irgendwann mal überschreiten sollte, ist das dann auch ein Buffer-Overflow? Und was passiert dann?
so etwas in der Art. Es ist eigentlich eine Aufgabe des Kernels, Stacksoverflows zu verhindern und darauf zu reagieren, meinstens durch eine Exception. Aber das ist Architektur abhänhig, d.h. jede Architektur hat da seine Tricks, mit denen man solche Sachen überpfüfen kann.
-
vista schrieb:
der stack-pointer ist einfach nur ein zeiger. wenn sein wertebereich um 1 überschritten wird, steht der wieder auf 0. und umgekehrt, ist er 0 und man führt einen POP- oder RET-befehl aus, dann dann hat er plötzlich einen irre hohen wert.
Du meinst vermutlich push und call. Der Stack Pointer arbeitet absteigend, dh immer wenn etwas auf dem Stack gespeichert wird, wird er verringert.
-
groovemaster schrieb:
vista schrieb:
der stack-pointer ist einfach nur ein zeiger. wenn sein wertebereich um 1 überschritten wird, steht der wieder auf 0. und umgekehrt, ist er 0 und man führt einen POP- oder RET-befehl aus, dann dann hat er plötzlich einen irre hohen wert.
Du meinst vermutlich push und call. Der Stack Pointer arbeitet absteigend, dh immer wenn etwas auf dem Stack gespeichert wird, wird er verringert.
stimmt. bei vielen prozessoren arbeitet der stack tatsächlich rückwärts.
weiss eigentlich jemand warum das so ist?
-
vista schrieb:
groovemaster schrieb:
vista schrieb:
der stack-pointer ist einfach nur ein zeiger. wenn sein wertebereich um 1 überschritten wird, steht der wieder auf 0. und umgekehrt, ist er 0 und man führt einen POP- oder RET-befehl aus, dann dann hat er plötzlich einen irre hohen wert.
Du meinst vermutlich push und call. Der Stack Pointer arbeitet absteigend, dh immer wenn etwas auf dem Stack gespeichert wird, wird er verringert.
stimmt. bei vielen prozessoren arbeitet der stack tatsächlich rückwärts.
weiss eigentlich jemand warum das so ist?Tja vielleicht eine Frage der "Sichtweise", das Wort sollte ja vom lateinischen Wort für "Kammer" abstammen. Wenn du was in eine Kammer reinstellst fängst du ja auch am Ende an und dann stellst du mehr Zeug in deiner Richtung rein.
Wenn du dann also z.B. jetzt die Länge der Kammer nimmst und jeder Zentimeter eine Adresse darstellt dann fängst du bei der höchstmöglichen Adresse an und je mehr du reinstellst desto kleiner werden die Adressen.Also praktisch wenn du auf einen Stack (Tellerstapel oder ähnliches) draufschaust dann "wächst" er zu dir und wenn du ein Element rausnimmst verkleinert er sich "weg" von dir. *schmunzl*.
Aber so gesehen, keine Ahnung
tt
-
vista schrieb:
stimmt. bei vielen prozessoren arbeitet der stack tatsächlich rückwärts.
weiss eigentlich jemand warum das so ist?Der Heap wächst aufwärts und der Stack abwärts. Solange sie sich nicht in der Mitte treffen ist alles in Butter ...
Jedenfalls war das früher mal so, ob das heute auch noch so ist oder ob sich die beiden nicht mehr in die Quere kommen können weiß ich nicht.