I libri di programmazione spiegano che i tipi di valore sono creati sul stack, e i tipi di riferimento sono creati sul heap, senza spiegare cosa sono queste due cose. Non ho letto una spiegazione chiara di questo. Capisco cos'è uno stack. Ma,
Stack:
Heap:
delete
, delete[]
, o free
.new
o malloc
.Esempio:
linguaggio: c++ -->
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;
**La pila Quando chiamate una funzione, gli argomenti di quella funzione, più qualche altra spesa, vengono messi sullo stack. Anche alcune informazioni (come dove andare al ritorno) sono memorizzate lì. Quando dichiarate una variabile all'interno della vostra funzione, anche quella variabile viene allocata sullo stack.
Deallocare lo stack è abbastanza semplice perché lo si dealloca sempre nell'ordine inverso in cui si alloca. La roba sullo stack viene aggiunta quando si entra nelle funzioni, i dati corrispondenti vengono rimossi quando si esce da esse. Questo significa che si tende a rimanere all'interno di una piccola regione dello stack, a meno che non si chiamino molte funzioni che chiamano molte altre funzioni (o si crei una soluzione ricorsiva).
L'Heap L'heap è un nome generico per dove si mettono i dati che si creano al volo. Se non sapete quante astronavi il vostro programma sta per creare, è probabile che usiate l'operatore new (o malloc o equivalente) per creare ogni astronave. Questa allocazione resterà in giro per un po', quindi è probabile che libereremo le cose in un ordine diverso da quello in cui le abbiamo create.
Così, l'heap è molto più complesso, perché ci sono regioni di memoria che sono inutilizzate e pezzi che lo sono - la memoria viene frammentata. Trovare memoria libera della dimensione necessaria è un problema difficile. Questo è il motivo per cui l'heap dovrebbe essere evitato (anche se è ancora spesso usato).
Implementazione L'implementazione sia dello stack che dell'heap è di solito compito del runtime / OS. Spesso i giochi e altre applicazioni che sono critiche per le prestazioni creano le proprie soluzioni di memoria che prendono una grande porzione di memoria dall'heap e poi la distribuiscono internamente per evitare di fare affidamento sul sistema operativo per la memoria.
Questo è pratico solo se l'uso della memoria è abbastanza diverso dalla norma - cioè per i giochi in cui si carica un livello in un'operazione enorme e si può buttare via tutto in un'altra operazione enorme.
Posizione fisica nella memoria Questo è meno rilevante di quanto pensiate a causa di una tecnologia chiamata Memoria Virtuale che fa credere al vostro programma di avere accesso a un certo indirizzo mentre i dati fisici sono da qualche altra parte (anche sul disco rigido!). Gli indirizzi che ottenete per lo stack sono in ordine crescente man mano che il vostro albero delle chiamate diventa più profondo. Gli indirizzi per l'heap sono imprevedibili (cioè specifici dell'implementazione) e francamente non sono importanti.
Lo stack è una porzione di memoria che può essere manipolata attraverso diverse istruzioni chiave del linguaggio assembly, come 'pop' (rimuovere e restituire un valore dallo stack) e 'push' (spingere un valore sullo stack), ma anche call (chiamare una subroutine - questo spinge l'indirizzo per tornare sullo stack) e return (ritorno da una subroutine - questo pops l'indirizzo dallo stack e salta ad esso). È la regione di memoria sotto il registro del puntatore dello stack, che può essere impostato come necessario. Lo stack è anche usato per passare argomenti alle subroutine, e anche per conservare i valori nei registri prima di chiamare le subroutine.
L'heap è una porzione di memoria che viene data ad un'applicazione dal sistema operativo, tipicamente attraverso una syscall come malloc. Sui sistemi operativi moderni questa memoria è un insieme di pagine a cui solo il processo chiamante ha accesso.
La dimensione dello stack è determinata a runtime, e generalmente non cresce dopo il lancio del programma. In un programma C, lo stack deve essere abbastanza grande da contenere ogni variabile dichiarata all'interno di ogni funzione. L'heap crescerà dinamicamente come necessario, ma il sistema operativo in definitiva sta facendo la chiamata (spesso farà crescere l'heap di più del valore richiesto da malloc, in modo che almeno alcuni malloc futuri non avranno bisogno di tornare al kernel per ottenere più memoria. Questo comportamento è spesso personalizzabile)
Poiché avete allocato lo stack prima di lanciare il programma, non avete mai bisogno di malloc prima di poter utilizzare lo stack, quindi questo è un leggero vantaggio. In pratica, è molto difficile prevedere cosa sarà veloce e cosa sarà lento nei sistemi operativi moderni che hanno sottosistemi di memoria virtuale, perché come le pagine sono implementate e dove sono memorizzate è un dettaglio di implementazione.