allgemeine (Anfänger-)Fragen zu Heap und Stack
-
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.
-
Bashar schrieb:
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.Ich bin mir nicht ganz sicher ob man dass als Grund angeben kann. Wenn ich mich recht entsinne liegt z.B. bei Linux der Stack "vor" dem Heap und wächst Richtung Heap und der Heap in Richtung Stack.
Wobei ist es überhaupt relevant bei Systemen mit "segmentbasierter" Speicherverwaltung? Da müssen ja nur die Segmentstartadressen so gelegt sein das der Stack z.B. weit genug "davor" liegt.Bei Windows (XP) bin ich mir übrigens gar nicht so sicher wo der Heap liegt, dort scheint es so als ob Code, Text usw. "zwischen" Stack und Heap liegt, die beiden sich also sowieso nicht "sehen".
tt
-
http://chaosradio.ccc.de/media/video/der_stack.m4v
Das wird alle eure Fragen erklären. Grade bei Wikipedia gefunden. Könnte eventuell da einer rausnehmen, ist ja nichts informatives. Naja deie Leute da haben auch offensichltich keine Ahnung.
Und das ganze Stack/Heap-Gelaber könnt ihr euch eigentlich sparen, ist alles von der Platform abhängig. Wenn ich mit eurer x86 Diskussion fertig seid, fang ich mit meinem AVR an.
-
Uiuiui, da programmiert einer AVRs *verneig*
-
TheTester schrieb:
Bei Windows (XP) bin ich mir übrigens gar nicht so sicher wo der Heap liegt, dort scheint es so als ob Code, Text usw. "zwischen" Stack und Heap liegt, die beiden sich also sowieso nicht "sehen".
unter xp hat jeder thread seinen eigenen stack und jeder prozess bekommt einen heap zugeteilt, den sich alle threads teilen können. weil xp sowieso mit virtuellen adressen arbeitet, kann der speicher dafür 'irgendwo' sein. der stackpointer 'wächst' aber trotzdem nach unten. warum?
Entenwickler schrieb:
Und das ganze Stack/Heap-Gelaber könnt ihr euch eigentlich sparen, ist alles von der Platform abhängig. Wenn ich mit eurer x86 Diskussion fertig seid, fang ich mit meinem AVR an.
die kleinen avr's haben nur einen 8bit stackpointer, die grösseren 16bits. bei beiden sollte bei der initialisierung der SP auf die höchste adresse des internen RAMs gesetzt werden d.h. der stack zählt auch abwärts. warum nur?
btw: beim ollen 6502 hatte der stack einen festen bereich zwichen 0x100 und 0x1ff. auch hier: initialisierung mit 0x1ff und dann geht's runter bei PUSHes und CALLs. merkwürdig, nicht?