Stack e heap - domanda di allocazione dinamica

6

Le fonti di solito menzionano che le variabili create dinamicamente sono allocate nell'heap, mentre le variabili delle funzioni nello stack. Anche quelli in pila cessano di esistere automaticamente quando ad es. la funzione che contiene la variabile esce. Se ho capito bene, i campi di dati della classe sono nell'heap. Non capisco, cosa si intende per "dinamico"? A mio avviso, quando il codice è in esecuzione, tutto ciò che viene creato viene creato dinamicamente al volo, sia esso variabili di funzione o oggetti con variabili al loro interno. Sarei felice per una semplice spiegazione. Grazie

    
posta John V 10.01.2013 - 16:53
fonte

4 risposte

4

Sono d'accordo sul fatto che "dinamico" sia una scelta scadente di parole, ma ora siamo bloccati. Significa fondamentalmente che l'allocazione della memoria viene effettuata esplicitamente dal programmatore, usando qualcosa come new o malloc , invece che automaticamente dal linguaggio di programmazione, come nelle variabili locali di una funzione.

    
risposta data 10.01.2013 - 17:10
fonte
2

Nella maggior parte dei contesti, "dinamico" si riferisce alla memoria allocata dal programmatore esplicitamente , a differenza della memoria allocata come parte normale dell'inserimento di una funzione (riservando spazio per i parametri, variabili locali , eccetera.).

Prendiamo come esempio la seguente funzione C:

void foo(int x, int y)
{
  int z;
  char *str = NULL;
  ...
  str = malloc(sizeof *str * y);
  ...
  free(str);
}

Quando entriamo nella funzione, lo spazio viene automaticamente prenotato nello stack per x , y , z e il puntatore str (ricorda che gli elementi dello stack sono allocati nell'ultimo, primo ordine ). Inoltre, la quantità di spazio da riservare per ogni oggetto è fissata al momento della compilazione (in base alle dimensioni dei rispettivi tipi). Se guardi il frame dello stack, sarà simile a questo (supponiamo che int e char * siano tutte della stessa dimensione): %pr_e%

Quando si esce dalla funzione, tutti gli elementi tra "STACK BOTTOM" e "STACK TOP" verranno spuntati (deallocati).

Non controllo l'allocazione della memoria a questo livello; tutto questo viene fatto automaticamente per me quando entro e esco dalla funzione. Tuttavia, all'interno della funzione voglio allocare memoria per str a cui puntare. Inoltre, la quantità di memoria che desidero allocare non è fissa, ma viene determinata in fase di esecuzione dal valore del parametro y . La chiamata malloc riserva un po 'di memoria dall'heap (un'area di memoria distinta dallo stack, che viene gestita in modo diverso) e assegna l'indirizzo di quella memoria a str . Quella memoria rimane allocata finché non la rilascio esplicitamente con una chiamata a free ; se non lo faccio prima che la funzione esca, la variabile str variabile viene deallocata (significa che perdo il mio riferimento a quel blocco assegnato in modo dinamico), ma la memoria a cui punta non lo fa (questo è noto come una perdita di memoria, una volta che ho perso il mio riferimento al blocco dinamico, non posso free esso).

Java si comporta in modo leggermente diverso:

+--------+
|        | <-- y           STACK BOTTOM
+--------+
|        | <-- x
+--------+
|        | <-- z
+--------+
|        | <--str          STACK TOP
+--------+

Simile a C, Java riserva spazio nello stack per ogni x , y , z e un puntatore str (sì, Java usa i puntatori sotto il cofano, non lo fa esporre le operazioni del puntatore al programmatore). Simile a C, l'operatore new riserva la memoria per l'oggetto String sull'heap in fase di runtime. A differenza di C, Java monitora gli oggetti sull'heap per vedere se vengono referenziati da chiunque (come la variabile str ). Una volta che la funzione Java termina, tutti i riferimenti alla memoria dinamica cessano di esistere e un garbage collector automatico alla fine rilascia la memoria, quindi non devo liberarlo esplicitamente (infatti, Java non fornisce un equivalente al free funzione).

    
risposta data 10.01.2013 - 19:10
fonte
0

Dynamic praticamente significa che il numero di byte non è noto fino al runtime:

 void f(int a) {
    int *array=new int[a];
    ...
 }
 int main() {
   f(10);
   f(22);
 }

Avere entrambi f (10) ed f (22) supportati (senza istanze separate di f) significa che la dimensione del blocco di memoria cambia sul runtime.

    
risposta data 10.01.2013 - 16:59
fonte
0

Dovresti pensare allo stack come un'ottimizzazione, un dettagli di implementazione . Sfortunatamente, l'astrazione che è la gestione della memoria tende a perdere. Alcune lingue perdono molto (sto guardando te C) e alcuni lo perdono ... meno (cioè Java).

Quando hai bisogno di memoria, per impostazione predefinita, puoi immaginare che tutto vada nell'heap. È possibile, in qualsiasi circostanza, utilizzare la memoria heap per risolvere tutti i problemi. Le allocazioni nell'heap possono essere di una dimensione sconosciuta fino a quando non sono necessarie e possono vivere per un periodo di tempo indeterminato. Consentire queste qualità ha un costo però. Diverse lingue lo gestiscono in modo diverso, ma il punto rimane che il tempo e gli sforzi vengono spesi dal linguaggio, in qualche modo, per allocare, deallocare e potenzialmente deframmentare la memoria nell'heap.

Non tutta la memoria necessaria ha bisogno di quel livello di flessibilità. Esistono numerosi contesti comuni molto nella programmazione per computer in cui la memoria necessaria è di una dimensione nota in fase di compilazione e con un ambito che contiene determinate proprietà ben definite. Quando chiami un metodo, conosci che le informazioni in memoria che rappresentano l'elenco dei parametri, il valore di ritorno e le variabili locali hanno una dimensione fissa, e sai che tutte le nuove allocazioni di memoria lasceranno ambito prima di qualsiasi memoria memorizzata nel momento in cui è stata allocata. Questo segue l'esatta implementazione della struttura dati nota come Stack.

Uno stack, come struttura generale dei dati, è il punto in cui l'ultimo è il primo. Spingi gli oggetti su di esso e poi fai fuori oggetti dall'oggetto. L'elemento inserito è l'elemento più recente inserito (che è ancora nella struttura). Quindi supponiamo che ci sia uno stack che trattiene la memoria. Quando iniziamo l'esecuzione di un nuovo metodo, sappiamo che la memoria che assegniamo per quel metodo (tutti i metodi hanno bisogno di memoria per i loro argomenti, valore di ritorno e variabili locali). Se spingiamo tutta la memoria nello stack, sarà necessario espellerla prima che gli oggetti "sopra" nello stack possano essere spinti fuori. Questo va bene. Sappiamo che il ricordo del metodo che ha chiamato "noi" non avrà bisogno di essere liberato fino a quando non avremo finito, e sappiamo che tutti i metodi che chiameremo avranno la memoria sia assegnata che liberata prima che sia necessario liberare.

La gestione di questo tipo di memoria è molto più semplice della gestione della memoria heap. È molto facile e veloce aggiungere nuova memoria allo stack e molto facile da rimuovere. Lo "stack" è in realtà solo un lungo blocco contiguo di memoria con puntatore alla "fine" dello stack. L'allocazione di nuova memoria è semplice come aumentare il puntatore dello stack e comunicare a qualcuno a quali indirizzi è consentito l'accesso e deallocare è semplice come diminuire il puntatore dello stack di un valore fisso. Aggiungere / sottrarre un valore fisso a un puntatore è molto facile e veloce per un computer.

Quindi l'intero scopo dello stack non è quello di consentire funzionalità aggiuntive; infatti, l'utilizzo dello stack è più restrittivo rispetto all'utilizzo dell'heap e i programmi più complessi tendono ad avere almeno alcune situazioni in cui le restrizioni della memoria dello stack sono proibitive, quindi sarà comunque necessario un heap. La memoria dello stack consente semplicemente un'allocazione e una deallocazione molto più veloci di determinati usi della memoria che soddisfano le restrizioni di uno stack.

    
risposta data 10.01.2013 - 18:38
fonte

Leggi altre domande sui tag