Vraag Wat en waar zijn de stapel en de hoop?


Het programmeren van taalboeken legt uit dat er waardetypen worden gemaakt op de stack, en referentietypes worden gemaakt op de hoop, zonder uit te leggen wat deze twee dingen zijn. Ik heb hier geen duidelijke verklaring voor gelezen. Ik begrijp wat een stapel is. Maar,

  • waar en wat zijn ze (fysiek in het geheugen van een echte computer)?
  • In hoeverre worden ze bestuurd door het besturingssysteem of de runtime van de taal?
  • Wat is hun reikwijdte?
  • Wat bepaalt de grootte van elk van hen?
  • Wat maakt iemand sneller?

7116
2017-09-17 04:18


oorsprong


antwoorden:


De stapel is het geheugen dat wordt gereserveerd als kladruimte voor een thread van uitvoering. Wanneer een functie wordt aangeroepen, wordt een blok bovenaan de stapel gereserveerd voor lokale variabelen en sommige boekhoudgegevens. Wanneer die functie terugkeert, wordt het blok ongebruikt en kan het de volgende keer dat een functie wordt aangeroepen worden gebruikt. De stapel is altijd gereserveerd in een LIFO (last in first out) volgorde; het meest recent gereserveerde blok is altijd het volgende blok dat vrijgemaakt moet worden. Dit maakt het heel eenvoudig om de stapel bij te houden; het vrijmaken van een blok van de stapel is niets meer dan het aanpassen van een aanwijzer.

De heap is geheugen gereserveerd voor dynamische toewijzing. In tegenstelling tot de stapel is er geen gedwongen patroon voor het toewijzen en deallocatie van blokken van de heap; je kunt op elk moment een blok toewijzen en het op elk moment vrijmaken. Dit maakt het veel complexer om bij te houden welke delen van de heap op elk moment zijn toegewezen of vrij zijn; er zijn veel aangepaste heap-allocators beschikbaar om de heap-prestaties af te stemmen op verschillende gebruikspatronen.

Elke thread krijgt een stapel, terwijl er meestal maar één heap is voor de applicatie (hoewel het niet ongebruikelijk is om meerdere heaps te hebben voor verschillende soorten toewijzingen).

Om uw vragen rechtstreeks te beantwoorden:

In hoeverre worden ze bestuurd door het besturingssysteem of de runtime van de taal?

Het besturingssysteem wijst de stapel toe voor elke thread op systeemniveau wanneer de thread wordt gemaakt. Doorgaans wordt het besturingssysteem opgeroepen door de taalruntime om de heap voor de toepassing toe te wijzen.

Wat is hun reikwijdte?

De stapel is aan een draad bevestigd, dus wanneer de draad wordt verlaten, wordt de stapel teruggewonnen. De heap wordt meestal toegewezen aan het opstarten van de toepassing door de runtime en wordt teruggevorderd wanneer de toepassing (technisch proces) wordt afgesloten.

Wat bepaalt de grootte van elk van hen? 

De grootte van de stapel wordt ingesteld wanneer een thread wordt gemaakt. De grootte van de heap wordt ingesteld bij het opstarten van de toepassing, maar kan groeien als er ruimte nodig is (de toewijzer vraagt ​​meer geheugen van het besturingssysteem).

Wat maakt iemand sneller?

De stack is sneller omdat het toegangspatroon het triviaal maakt om geheugen toe te wijzen en de toewijzing ervan te ontkoppelen (een pointer / geheel getal wordt eenvoudigweg opgehoogd of verlaagd), terwijl de heap een veel complexere boekhouding heeft bij een toewijzing of deallocatie. Elke byte in de stapel heeft ook de neiging zeer vaak opnieuw te worden gebruikt, wat betekent dat deze meestal in kaart wordt gebracht in de cache van de processor, waardoor deze erg snel wordt. Een andere prestatiehit voor de heap is dat de heap, meestal een globale bron, doorgaans veilig moet zijn voor meerdere threads, d.w.z. elke toewijzing en deallocatie moet - typisch - worden gesynchroniseerd met "alle" andere heap-toegangen in het programma.

Een duidelijke demonstratie:
Afbeeldingsbron: vikashazrati.wordpress.com


5228
2017-09-17 04:52



stack:

  • Opgeslagen in computer-RAM, net als de heap.
  • Variabelen die op de stapel zijn gemaakt, vallen buiten het bereik en worden automatisch de toewijzing ongedaan gemaakt.
  • Veel sneller te alloceren in vergelijking met variabelen op de heap.
  • Geïmplementeerd met een werkelijke stackdatastructuur.
  • Slaat lokale gegevens op, geeft adressen terug en wordt gebruikt voor het doorgeven van parameters.
  • Kan een stapel overlopen wanneer te veel van de stapel wordt gebruikt (meestal van oneindige of te diepe recursie, zeer grote toewijzingen).
  • Gegevens die op de stapel zijn gemaakt, kunnen zonder aanwijzers worden gebruikt.
  • U zou de stapel gebruiken als u precies weet hoeveel gegevens u moet toewijzen vóór het compileren en het is niet te groot.
  • Meestal heeft een maximale grootte al bepaald wanneer uw programma start.

Hoop:

  • Opgeslagen in computer-RAM, net als de stapel.
  • In C ++ moeten variabelen op de heap handmatig worden vernietigd en vallen ze nooit buiten het bereik. De gegevens zijn bevrijd delete, delete[]of free.
  • Langzamer om toe te wijzen in vergelijking met variabelen op de stapel.
  • Wordt op aanvraag gebruikt om een ​​gegevensblok toe te wijzen voor gebruik door het programma.
  • Kan fragmentatie hebben als er veel toewijzingen en deallocaties zijn.
  • In C ++ of C worden gegevens die op de heap zijn gemaakt naar pointers verwezen en toegewezen new of malloc respectievelijk.
  • Kan allocatiefouten hebben als een te grote buffer wordt gevraagd om te worden toegewezen.
  • U zou de heap gebruiken als u niet precies weet hoeveel gegevens u tijdens runtime nodig heeft of als u veel gegevens moet toewijzen.
  • Verantwoordelijk voor geheugenlekken.

Voorbeeld:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

2092
2017-09-17 04:20



Het belangrijkste punt is dat heap en stack generieke termen zijn voor manieren waarop geheugen kan worden toegewezen. Ze kunnen op veel verschillende manieren worden geïmplementeerd en de voorwaarden zijn van toepassing op de basisbegrippen.

  • In een stapel items zitten items op elkaar in de volgorde waarin ze daar zijn geplaatst, en je kunt alleen de bovenste verwijderen (zonder het hele ding omver te werpen).

    Stack like a stack of papers

    De eenvoud van een stapel is dat u geen tabel met een record van elk gedeelte van het toegewezen geheugen hoeft bij te houden; de enige statusinformatie die u nodig hebt, is een enkele aanwijzer naar het einde van de stapel. Als u de toewijzing wilt toewijzen en de toewijzing ongedaan wilt maken, verhoogt en verlaagt u die ene aanwijzer. Opmerking: een stapel kan soms worden geïmplementeerd om bovenaan een gedeelte van het geheugen te beginnen en naar beneden uit te strekken in plaats van omhoog te groeien.

  • In een heap is er geen specifieke volgorde aan de manier waarop items worden geplaatst. Je kunt items in willekeurige volgorde bereiken en verwijderen, omdat er geen duidelijk 'top'-item is.

    Heap like a heap of licorice allsorts

    Heap-toewijzing vereist dat een volledig overzicht wordt bijgehouden van welk geheugen wordt toegewezen en wat niet, evenals wat onderhoud van de overhead om fragmentatie te verminderen, samenhangende geheugensegmenten vindt die groot genoeg zijn voor de gevraagde grootte, enzovoort. Geheugen kan op elk moment worden verwijderd en laat ruimte vrij. Soms voert een geheugentoewijzer onderhoudstaken uit, zoals het defragmenteren van het geheugen door het verplaatsen van toegewezen geheugen of het verzamelen van afval - het identificeren tijdens runtime wanneer het geheugen niet langer in omvang is en het vrijgeven van het geheugen.

Deze afbeeldingen zouden een redelijk goede beschrijving moeten geven van de twee manieren om geheugen in een stapel en een hoop toe te wijzen en vrij te maken. Yum!

  • In hoeverre worden ze bestuurd door het besturingssysteem of de runtime van de taal?

    Zoals vermeld, zijn heap en stack algemene voorwaarden en kunnen op vele manieren worden geïmplementeerd. Computerprogramma's hebben meestal een stack met de naam a oproepstapel die informatie opslaat die relevant is voor de huidige functie, zoals een wijzer naar de functie van waaruit hij werd opgeroepen en eventuele lokale variabelen. Omdat functies andere functies aanroepen en vervolgens terugkeren, wordt de stapel groter en kleiner om informatie van de functies verderop in de oproepstapel vast te houden. Een programma heeft er niet echt runtime-controle over; het wordt bepaald door de programmeertaal, OS en zelfs de systeemarchitectuur.

    Een heap is een algemene term die wordt gebruikt voor elk geheugen dat dynamisch en willekeurig wordt toegewezen; dat wil zeggen niet in de juiste volgorde. Het geheugen wordt meestal toegewezen door het besturingssysteem, met API-functies voor het aanvragen van deze toewijzing. Er is behoorlijk wat overhead nodig voor het beheer van dynamisch toegewezen geheugen, dat meestal door het besturingssysteem wordt afgehandeld.

  • Wat is hun reikwijdte?

    De call stack is zo'n laag concept dat het geen betrekking heeft op 'scope' in de zin van programmeren. Als u sommige codes demonteert, ziet u relatieve verwijzing naar verwijzingsstijlen naar gedeelten van de stapel, maar voor zover het een hogere taal betreft, legt de taal zijn eigen regels op. Een belangrijk aspect van een stapel is echter dat zodra een functie terugkeert, alles wat lokaal is naar die functie, onmiddellijk wordt bevrijd van de stapel. Dat werkt zoals je zou verwachten, gezien de manier waarop je programmeertalen werken. In een heap is het ook moeilijk om te definiëren. Het toepassingsgebied is alles wat wordt blootgegeven door het besturingssysteem, maar uw programmeertaal voegt waarschijnlijk regels toe over wat een "bereik" is in uw toepassing. De processorarchitectuur en het besturingssysteem maken gebruik van virtuele adressering, die de processor vertaalt naar fysieke adressen en er zijn paginafouten, enz. Ze houden bij welke pagina's bij welke toepassingen horen. U hoeft hier echter nooit echt zorgen over te maken, omdat u gewoon de methode gebruikt die uw programmeertaal gebruikt om geheugen toe te wijzen en vrij te maken, en om te controleren op fouten (als de toewijzing / bevrijding om welke reden dan ook mislukt).

  • Wat bepaalt de grootte van elk van hen?

    Nogmaals, het hangt af van de taal, compiler, besturingssysteem en architectuur. Een stapel is meestal vooraf toegewezen, omdat het per definitie een aangrenzend geheugen moet zijn (meer hierover in de laatste alinea). De taalcompiler of het besturingssysteem bepalen de grootte. U slaat geen enorme hoeveelheden gegevens op de stapel op, dus deze zullen groot genoeg zijn om nooit volledig te worden gebruikt, behalve in gevallen van ongewenste eindeloze recursie (vandaar "stapeloverloop") of andere ongebruikelijke programmeerbeslissingen.

    Een heap is een algemene term voor alles dat dynamisch kan worden toegewezen. Afhankelijk van de manier waarop je ernaar kijkt, verandert het voortdurend van grootte. In moderne processors en besturingssystemen is de manier waarop het werkt hoe dan ook erg geabstraheerd, dus je hoeft je normaal gesproken niet veel zorgen te maken over hoe het werkt, behalve dat (in talen waar het je laat) je geen geheugen moet gebruiken dat je hebt nog geen toegewezen geheugen of geheugen dat je hebt vrijgemaakt.

  • Wat maakt iemand sneller?

    De stapel is sneller omdat alle vrije geheugen altijd aaneengesloten is. Er hoeft geen lijst te worden bijgehouden van alle segmenten van vrij geheugen, slechts één aanwijzer naar de huidige bovenkant van de stapel. Compilers bewaren deze aanwijzer meestal in een speciale, snel registreren Voor dit doeleinde. Bovendien zijn de volgende bewerkingen op een stapel meestal geconcentreerd in zeer nabije gebieden van het geheugen, die op een zeer laag niveau goed zijn voor optimalisatie door de processor-on-die-caches.


1259
2018-03-19 14:38



(Ik heb dit antwoord verplaatst van een andere vraag die min of meer een dupe van deze was.)

Het antwoord op uw vraag is implementatiespecifiek en kan verschillen tussen compilers en processorarchitecturen. Hier is echter een vereenvoudigde uitleg.

  • Zowel de stack als de heap zijn geheugengebieden die zijn toegewezen aan het onderliggende besturingssysteem (vaak virtueel geheugen dat op aanvraag wordt toegewezen aan fysiek geheugen).
  • In een omgeving met meerdere threads heeft elke thread zijn eigen volledig onafhankelijke stapel, maar delen ze de hoop. Gelijktijdige toegang moet op de hoop worden beheerd en is niet mogelijk op de stapel.

De hoop

  • De heap bevat een gelinkte lijst met gebruikte en gratis blokken. Nieuwe toewijzingen op de hoop (door new of malloc) zijn tevreden door een geschikt blok te maken uit een van de vrije blokken. Dit vereist een lijst met blokken bijwerken op de heap. Deze Meta informatie over de blokken op de hoop wordt ook opgeslagen op de hoop vaak in een klein gebied vlak voor elk blok.
  • Naarmate de hoop groeit, worden nieuwe blokken vaak toegewezen van lagere adressen naar hogere adressen. Dus je kunt de hoop zien als een hoop van geheugenblokken die groter worden naarmate het geheugen wordt toegewezen. Als de heap te klein is voor een toewijzing, kan de grootte vaak worden verhoogd door meer geheugen te verkrijgen van het onderliggende besturingssysteem.
  • Het toewijzen en de toewijzing van veel kleine blokken kan de heap verlaten in een staat waar er veel kleine vrije blokken zijn afgewisseld tussen de gebruikte blokken. Een verzoek om een ​​groot blok toe te wijzen kan falen omdat geen van de vrije blokken groot genoeg is om te voldoen aan het toewijzingsverzoek, hoewel de gecombineerde grootte van de vrije blokken groot genoeg kan zijn. Dit heet heap fragmentatie.
  • Wanneer een gebruikt blok dat grenst aan een vrij blok wordt deallocated, kan het nieuwe vrije blok worden samengevoegd met het aangrenzende vrije blok om een ​​groter vrij blok te creëren dat effectief de fragmentatie van de hoop vermindert.

The heap

De stapel

  • De stapel werkt vaak in een nauwe tandem met een speciaal register op de CPU genaamd de stapel aanwijzer. Aanvankelijk wijst de stapelwijzer naar de bovenkant van de stapel (het hoogste adres op de stapel).
  • De CPU heeft speciale instructies voor voortvarend waarden op de stapel en popping ze terug van de stapel. Elk Duwen slaat de waarde op de huidige locatie van de stapelwijzer op en verlaagt de stapelwijzer. EEN knal haalt de waarde op waarnaar wordt verwezen door de stapel-aanwijzer en verhoogt vervolgens de stapel-aanwijzer (laat u niet verwarren door het feit dat toe te voegen een waarde voor de stapel vermindert de stapel-aanwijzer en Verwijderen een waarde toeneemt het. Vergeet niet dat de stapel naar de bodem toe groeit). De opgeslagen en opgehaalde waarden zijn de waarden van de CPU-registers.
  • Wanneer een functie wordt aangeroepen, gebruikt de CPU speciale instructies die de stroom pushen instructie pointer, d.w.z. het adres van de code die op de stapel wordt uitgevoerd. De CPU springt vervolgens naar de functie door de instructie pointer naar het adres van de functie genaamd. Later, wanneer de functie terugkeert, wordt de oude instructiepointer uit de stapel gehaald en wordt het uitvoeren hervat met de code net na de aanroep van de functie.
  • Wanneer een functie wordt ingevoerd, wordt de stapelwijzer verlaagd om meer ruimte toe te wijzen aan de stapel voor lokale (automatische) variabelen. Als de functie één lokale 32-bits variabele heeft, worden er vier bytes op de stapel geplaatst. Wanneer de functie terugkeert, wordt de stapelwijzer terug verplaatst om het toegewezen gebied vrij te maken.
  • Als een functie parameters heeft, worden deze op de stapel gedrukt voordat de functie wordt aangeroepen. De code in de functie kan dan de stapel opzoeken van de huidige stapelwijzer om deze waarden te vinden.
  • Nesten van functieaanroepen werkt zo charmant. Elke nieuwe oproep zal functieparameters, het retouradres en ruimte voor lokale variabelen en deze toewijzen activeringsrecords kan worden gestapeld voor geneste oproepen en zal op de juiste manier afwikkelen wanneer de functies terugkeren.
  • Omdat de stapel een beperkt geheugenblok is, kunt u een stapel overloop door te veel geneste functies aan te roepen en / of te veel ruimte toe te wijzen aan lokale variabelen. Vaak is het geheugengebied dat wordt gebruikt voor de stapel zodanig opgezet dat het schrijven onder de onderste (het laagste adres) van de stapel een trap of uitzondering in de CPU veroorzaakt. Deze uitzonderlijke conditie kan dan worden opgevangen door de runtime en worden omgezet in een of andere uitzondering voor stackoverloop.

The stack

Kan een functie op de heap worden toegewezen in plaats van op een stapel?

Nee, activeringsrecords voor functies (dat wil zeggen lokale of automatische variabelen) worden toegewezen aan de stapel die niet alleen wordt gebruikt voor het opslaan van deze variabelen, maar ook voor het bijhouden van geneste functie-aanroepen.

Hoe de heap wordt beheerd, hangt helemaal af van de runtime-omgeving. C gebruikt malloc en C ++ gebruikt new, maar veel andere talen hebben garbagecollection.

De stack is echter een functie op een lager niveau die nauw verbonden is met de processorarchitectuur. Het groeien van de heap wanneer er niet genoeg ruimte is, is niet zo moeilijk, omdat het kan worden geïmplementeerd in de bibliotheekoproep die de heap afhandelt. Het laten groeien van de stapel is echter vaak onmogelijk omdat de stapeloverloop alleen wordt ontdekt als het te laat is; en het afsluiten van de thread van uitvoering is de enige haalbare optie.


663
2017-07-31 15:54



In de volgende C # -code

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Hier leest u hoe het geheugen wordt beheerd

Picture of variables on the stack

Local Variables dat hoeft alleen maar te duren zolang de functie-aanroep in de stapel gaat. De heap wordt gebruikt voor variabelen waarvan we de levensduur niet echt kennen, maar we verwachten dat ze een tijdje mee gaan. In de meeste talen is het van cruciaal belang dat we bij het compileren weten hoe groot een variabele is als we deze op de stapel willen opslaan.

Objecten (die in grootte variëren als we ze bijwerken) gaan op de heap omdat we niet weten op het moment van creatie hoe lang ze zullen duren. In veel talen is de heap vuilnis verzameld om objecten (zoals het object cls1) te vinden die geen verwijzingen meer bevatten.

In Java gaan de meeste objecten rechtstreeks de stapel in. In talen zoals C / C ++ kunnen structs en klassen vaak op de stapel blijven staan ​​als je geen pointers hebt.

Meer informatie is hier te vinden:

Het verschil tussen stack- en heapgeheugentoewijzing «timmurphy.org

en hier:

Objecten in de stapel en hoop maken

Dit artikel is de bron van bovenstaande afbeelding: Zes belangrijke .NET-concepten: Stack, heap, waardetypen, referentietypes, boxen en unboxing - CodeProject

maar wees ervan bewust dat het enkele onnauwkeurigheden kan bevatten.


350
2017-11-09 12:28



De stapel Wanneer u een functie aanroept, worden de argumenten voor die functie plus enige andere overhead op de stapel geplaatst. Sommige informatie (zoals waar naartoe te gaan) wordt daar ook opgeslagen. Wanneer u een variabele in uw functie declareert, wordt die variabele ook aan de stapel toegewezen.

De toewijzing van de stapel is vrij eenvoudig, omdat u altijd de toewijzing opgeeft in de omgekeerde volgorde waarin u toewijst. Stapeldingen worden toegevoegd terwijl u functies invoert, de bijbehorende gegevens worden verwijderd wanneer u ze afsluit. Dit betekent dat je de neiging hebt om in een klein deel van de stapel te blijven, tenzij je veel functies oproept die veel andere functies aanroepen (of een recursieve oplossing maken).

The Heap De heap is een generieke naam voor de plek waar je de gegevens die je maakt tijdens het werk neerzet. Als je niet weet hoeveel ruimteschepen je programma gaat maken, gebruik je waarschijnlijk de nieuwe (of malloc of gelijkwaardige) operator om elk ruimteschip te maken. Deze toewijzing zal nog een tijdje blijven hangen, dus het is waarschijnlijk dat we dingen in een andere volgorde zullen bevrijden dan we ze hebben gemaakt.

Dus, de heap is veel complexer, omdat er uiteindelijk gebieden van geheugen zijn die ongebruikt zijn doorschoten met brokken die - het geheugen wordt gefragmenteerd. Het vinden van gratis geheugen van de grootte die u nodig hebt, is een moeilijk probleem. Dit is de reden waarom de hoop moet worden vermeden (hoewel het nog steeds vaak wordt gebruikt).

Implementatie De implementatie van zowel de stack als de heap is meestal te danken aan de runtime / OS. Vaak creëren games en andere applicaties die kritisch zijn voor de performance hun eigen geheugenoplossingen die een groot deel van het geheugen uit de heap halen en vervolgens intern indelen om te voorkomen dat ze afhankelijk zijn van het OS voor geheugen.

Dit is alleen praktisch als je geheugengebruik behoorlijk verschilt van de norm - dus voor spellen waarbij je een niveau laadt in één grote operatie en de hele boel weg kunt gooien in een andere enorme operatie.

Fysieke locatie in het geheugen Dit is minder relevant dan je denkt vanwege een technologie die wordt genoemd Virtueel geheugen waardoor uw programma denkt dat u toegang hebt tot een bepaald adres waar de fysieke gegevens zich ergens anders bevinden (zelfs op de harde schijf!). De adressen die u voor de stapel krijgt, zijn in oplopende volgorde als uw belboom dieper wordt. De adressen voor de heap zijn niet voorspelbaar (d.w.z. specifiek voor de uitvoering) en zijn eerlijk gezegd niet belangrijk.


190
2017-09-17 04:27



Verduidelijken, dit antwoord heeft onjuiste informatie (thomas bevestigde zijn antwoord na opmerkingen, cool :)). Andere antwoorden vermijden gewoon dat wordt uitgelegd wat statische toewijzing betekent. Dus ik zal de drie belangrijkste vormen van toewijzing uitleggen en hoe ze gewoonlijk betrekking hebben op het heap-, stapel- en gegevenssegment hieronder. Ik zal ook enkele voorbeelden laten zien in zowel C / C ++ als Python om mensen te helpen begrijpen.

"Statische" (statisch toegewezen AKA) variabelen worden niet toegewezen aan de stapel. Ga er niet van uit - veel mensen doen dat alleen omdat 'statisch' veel lijkt op 'stapel'. Ze bestaan ​​eigenlijk niet in de stapel noch in de hoop. Ze maken deel uit van wat de gegevenssegment.

Het is echter over het algemeen beter om te overwegen "strekking"en"levenslang"in plaats van" stapel "en" heap ".

Scope verwijst naar welke delen van de code toegang hebben tot een variabele. Over het algemeen denken we aan lokale bereik (kan alleen worden benaderd door de huidige functie) versus wereldwijde reikwijdte (overal toegankelijk), hoewel de reikwijdte veel complexer kan worden.

Levensduur verwijst naar wanneer een variabele wordt toegewezen en uitgezet tijdens de uitvoering van het programma. Meestal denken we aan statische toewijzing (variabele blijft bestaan ​​gedurende de volledige duur van het programma, waardoor het handig is om dezelfde informatie op te slaan over verschillende functie-aanroepen) versus automatische toewijzing (variabele blijft alleen bestaan ​​tijdens een enkele oproep naar een functie, waardoor het nuttig is voor het opslaan van informatie die alleen tijdens uw functie wordt gebruikt en kan worden weggegooid als u klaar bent) versus dynamische toewijzing (variabelen waarvan de duur wordt gedefinieerd tijdens runtime, in plaats van compileertijd zoals statisch of automatisch).

Hoewel de meeste compilers en tolken dit gedrag op dezelfde manier implementeren in termen van het gebruik van stacks, heaps, enz., Kan een compiler deze conventies soms breken als het dat wil, zolang het gedrag correct is. Als gevolg van optimalisatie kan een lokale variabele bijvoorbeeld alleen in een register bestaan ​​of volledig worden verwijderd, hoewel de meeste lokale variabelen in de stapel aanwezig zijn. Zoals in een paar opmerkingen is opgemerkt, staat het je vrij om een ​​compiler te implementeren die niet eens een stapel of een heap gebruikt, maar in plaats daarvan enkele andere opslagmechanismen (zelden gedaan, omdat stapels en stapels hiervoor geweldig zijn).

Ik zal een aantal eenvoudige geannoteerde C-codes verstrekken om dit allemaal te illustreren. De beste manier om te leren is om een ​​programma onder een debugger uit te voeren en het gedrag te bekijken. Als je liever Python leest, ga dan naar het einde van het antwoord :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Een bijzonder aangrijpend voorbeeld van waarom het belangrijk is om een ​​onderscheid te maken tussen levensduur en bereik, is dat een variabele een lokale scope kan hebben, maar een statische levensduur, bijvoorbeeld 'someLocalStaticVariable' in het codevoorbeeld hierboven. Dergelijke variabelen kunnen onze algemene maar informele naamgevingsgewoonten erg verwarrend maken. Bijvoorbeeld wanneer we zeggen "lokaal"wij bedoelen meestal"lokaal geschaalde automatisch toegewezen variabele"en wanneer we globaal zeggen, bedoelen we meestal"wereldwijd toegewezen statisch toegewezen variabele"Helaas als het gaat om zaken als"bestand scoped statisch toegewezen variabelen"veel mensen zeggen gewoon ..."huh ???".

Sommige van de syntaxiskeuzes in C / C ++ verergeren dit probleem - bijvoorbeeld veel mensen denken dat globale variabelen niet "statisch" zijn vanwege de syntaxis die hieronder wordt getoond.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Merk op dat het woord "static" in de bovenstaande verklaring voorkomt dat var2 een globale scope heeft. Niettemin heeft de globale var1 statische toewijzing. Dit is niet intuïtief! Om deze reden probeer ik nooit het woord "statisch" te gebruiken bij het beschrijven van de reikwijdte, en in plaats daarvan iets te zeggen als "file" of "file limited" scope. Veel mensen gebruiken echter de uitdrukking "statisch" of "statisch bereik" om een ​​variabele te beschrijven die alleen via één codebestand toegankelijk is. In de context van het leven, "statisch" altijd betekent dat de variabele wordt toegewezen bij het starten van het programma en wordt vrijgegeven wanneer het programma wordt afgesloten.

Sommige mensen beschouwen deze concepten als C / C ++ -specifiek. Zij zijn niet. Het hieronder afgebeelde Python-voorbeeld illustreert bijvoorbeeld alle drie typen toewijzing (er zijn enkele subtiele verschillen mogelijk in geïnterpreteerde talen waar ik hier niet op inga).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

168
2017-09-17 04:48



Anderen hebben de brede halen redelijk goed beantwoord, dus ik zal een paar details toevoegen.

  1. Stapel en hoop hoeven niet enkelvoudig te zijn. Een veelvoorkomende situatie waarin u meer dan één stapel hebt, is als u meer dan één thread in een proces hebt. In dit geval heeft elke thread zijn eigen stack. U kunt ook meer dan één heap hebben, bijvoorbeeld sommige DLL-configuraties kunnen ertoe leiden dat verschillende DLL's worden toegewezen uit verschillende heaps. Daarom is het over het algemeen een slecht idee om geheugen dat door een andere bibliotheek is toegewezen vrij te geven.

  2. In C kunt u profiteren van variabele lengtetoewijzing door het gebruik van alloca, die op de stapel toewijst, in tegenstelling tot toewijzing, die op de stapel wordt toegewezen. Dit geheugen zal je return-statement niet overleven, maar het is handig voor een scratch-buffer.

  3. Het maken van een enorme tijdelijke buffer op Windows waar je niet veel van gebruikt, is niet gratis. Dit komt omdat de compiler een stack probe-lus genereert die elke keer dat uw functie wordt ingevoerd wordt aangeroepen om ervoor te zorgen dat de stack bestaat (omdat Windows een enkele guard-pagina aan het einde van uw stack gebruikt om te detecteren wanneer de stack moet worden gegroeid. Als u meer dan één pagina van het einde van de stapel opslaat, loopt u vast). Voorbeeld:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

155
2017-09-17 07:16



Anderen hebben je vraag direct beantwoord, maar als ik de stapel en de hoop probeer te begrijpen, denk ik dat het nuttig is om de geheugenlay-out van een traditioneel UNIX-proces te overwegen (zonder threads en mmap()op basis van toewijzers). De Geheugenbeheer Woordenlijst webpagina heeft een diagram van deze geheugenlay-out.

De stapel en de heap bevinden zich traditioneel aan tegenovergestelde uiteinden van de virtuele adresruimte van het proces. De stapel groeit automatisch wanneer hij wordt geopend, tot een grootte die is ingesteld door de kernel (die kan worden aangepast met setrlimit(RLIMIT_STACK, ...)). De heap wordt groter wanneer de geheugentoewijzer de brk() of sbrk() systeemoproep, waarbij meer pagina's fysiek geheugen worden toegewezen aan de virtuele adresruimte van het proces.

In systemen zonder virtueel geheugen, zoals sommige ingebedde systemen, is vaak dezelfde basislay-out van toepassing, behalve dat de stapel en de heap vast in grootte zijn. In andere ingebedde systemen (zoals die op basis van Microchip PIC-microcontrollers), is de programmastack echter een afzonderlijk geheugenblok dat niet kan worden geadresseerd door instructies voor gegevensoverdracht en kan dit alleen indirect worden gewijzigd of gelezen via programmastroominstructies (oproep, terugkeer, etc.). Andere architecturen, zoals Intel Itanium-processors, hebben meerdere stapels. In die zin is de stack een element van de CPU-architectuur.


126
2017-09-17 04:57



Ik denk dat veel andere mensen je meestal correcte antwoorden hebben gegeven op deze kwestie.

Een detail dat is gemist, is echter dat de "heap" in feite waarschijnlijk de "gratis winkel" moet worden genoemd. De reden voor dit onderscheid is dat de oorspronkelijke gratis winkel is geïmplementeerd met een gegevensstructuur die bekend staat als een 'binomiale heap'. Om die reden was toewijzen vanaf malloc () / gratis () allocatie vanaf een heap. In deze moderne tijd worden de meeste gratis winkels geïmplementeerd met zeer uitgebreide gegevensstructuren die geen binomiale hopen zijn.


107
2017-09-17 04:29