2011-12-14 17 views
6

Non capisco il punto di memoria allocata dinamicamente e spero che voi ragazzi siate più chiari per me.Memoria allocata dinamicamente al C++

Prima di tutto, ogni volta che allochiamo memoria otteniamo semplicemente un puntatore a quella memoria.

int * dynInt = new int; 

Allora, qual è la differenza tra il fare quello che ho fatto sopra e:

int someInt; 
int* dynInt = &someInt; 

Se ho ben capito, in entrambi i casi memoria viene allocata per un int, e si ottiene un puntatore a quella memoria.

Quindi qual è la differenza tra i due. Quando è preferito un metodo all'altro.

ulteriormente più il motivo per cui ho bisogno di liberare la memoria con

delete dynInt; 

nel primo caso, ma non nel secondo caso.

mie supposizioni sono:

  1. Quando l'allocazione dinamica della memoria per un oggetto, l'oggetto non viene inizializzato, mentre se si fa qualcosa di simile nel secondo caso, l'ottenere l'oggetto del inizializzato. Se questa è l'unica differenza, c'è una motivazione dietro a questo, a parte il fatto che allocare dinamicamente la memoria è più veloce.

  2. La ragione per cui non è necessario utilizzare delete per il secondo caso è perché il fatto che l'oggetto sia stato inizializzato crea una sorta di routine di distruzione automatica.

Queste sono solo supposizioni che mi piacerebbe se qualcuno mi correggesse e chiarisse le cose per me.

+3

Se non sei sicuro, segui la semplice regola: "Non usare mai puntatori, non usare mai" nuovo "." Una volta compresa la necessità di una durata dell'oggetto gestita manualmente, saprai quando interrompere questa regola. –

+1

Uhm posso sollecitare vivamente l'utente ad aprire un libro sull'ambito delle variabili C++ .., la gestione dinamica della memoria viene generalmente discussa nel 4 ° o 5 ° capitolo di qualsiasi libro per principianti C++. @KerrekSB Non direi questo .. Soprattutto in questo caso l'incertezza non deriva dall'ambiguità, ma piuttosto da non abbastanza conoscenza - se non sei sicuro assicurati di aver letto abbastanza in modo da essere SICURO. – paul23

+0

L'allocazione dinamica della memoria è in genere _slower_ e l'inizializzazione non ha nulla a che fare con tutto ciò. –

risposta

15

La differenza è in durata di archiviazione.

  • oggetti con durata memorizzazione automatica sono gli oggetti "normali" che vanno automaticamente fuori portata alla fine del blocco in cui sono definiti.

    Creare come int someInt;

    Potresti averne sentito parlare come "oggetti stack", anche se ho oggetto a questa terminologia.

  • Gli oggetti con durata di archiviazione dinamica hanno una vita "manuale"; devi distruggerli tu stesso con delete e crearli con la parola chiave new.

    Potresti averne sentito parlare come "oggetti heap", anche se mi oppongo a questo.

L'utilizzo di puntatori in realtà non è strettamente pertinente per nessuno di essi. È possibile avere un puntatore a un oggetto di durata di archiviazione automatica (il secondo esempio) e si può avere un puntatore a un oggetto di durata di archiviazione dinamica (il primo esempio).

Ma è raro che si vorrà un puntatore ad un oggetto automatico, perché:

  1. non si dispone di un "default";
  2. l'oggetto non durerà molto a lungo, quindi non c'è molto che puoi fare con un tale puntatore.

Al contrario, gli oggetti dinamici sono spesso accessibili tramite puntatori, semplicemente perché la sintassi si avvicina a farla rispettare. new restituisce un puntatore da usare, devi passare un puntatore a delete e (a parte l'utilizzo di riferimenti) non c'è in realtà nessun altro modo per accedere all'oggetto. Vive "là fuori" in una nuvola di dinamicità che è non seduto nello scope locale.

Per questo motivo l'utilizzo dei puntatori viene a volte confuso con l'utilizzo dell'archiviazione dinamica, ma in realtà il primo non è causalmente correlato a quest'ultimo.

+0

I puntatori alle variabili automatiche possono essere utili quando si richiama una funzione che dovrebbe mutare i suoi argomenti (e forse usa il suo valore di ritorno per indicare il successo). –

+3

@ TamásSzelei: In questo caso dovresti usare un riferimento. –

+0

@ TamásSzelei: Beh, vero. Questo è un uso molto localizzato, però. Suppongo che sto parlando di ottenere un puntatore e di memorizzarlo, non usando un temporaneo in una singola espressione –

13

Un oggetto creato in questo modo:

int foo; 

ha durata memorizzazione automatica - l'oggetto vive fino a quando la variabile foo va fuori portata. Ciò significa che nel tuo primo esempio, dynInt sarà un puntatore non valido una volta che someInt si sposterà dall'ambito (ad esempio, alla fine di una funzione).

Un oggetto creato in questo modo:

int foo* = new int; 

Ha durata archiviazione dinamica - l'oggetto vive fino a quando si chiama in modo esplicito delete su di esso.

L'inizializzazione degli oggetti è un concetto ortogonale; non è direttamente correlato al tipo di spazio di archiviazione utilizzato. Vedere here per ulteriori informazioni sull'inizializzazione.

+0

Grazie per una risposta così buona e chiara. Grazie ancora. – vinay

+2

Per essere pedante, in realtà non si sa nulla della * memoria *, perché ciò dipende dall'implementazione della funzione di allocazione ':: operator new()'. È il * lifetime * dell'oggetto che è dinamico, cioè il manuale: L'oggetto * vive finché non lo si elimina. C++ separa l'allocazione della memoria e la costruzione dell'oggetto. –

+1

@KerrekSB: Sono sempre un po 'spaventato quando scrivo una simile risposta, perché sono sicuro di ottenere qualcosa di sbagliato :) –

1

Cosa succede se il programma deve consentire all'utente di memorizzare un numero qualsiasi di numeri interi? Quindi dovrai decidere durante l'esecuzione, in base all'input dell'utente, quanti intro allocare, quindi questo deve essere fatto dinamicamente.

+0

A meno che non usi [alloca] (http://stackoverflow.com/q/1018853/533120);) –

4

Per un numero intero singolo ha senso solo se è necessario mantenere il valore dopo, ad esempio, il ritorno da una funzione. Se avessi dichiarato someInt come hai detto, sarebbe stato invalidato non appena fosse uscito dall'ambito.

Tuttavia, in generale, è maggiore l'utilizzo dell'allocazione dinamica. Ci sono molte cose che il tuo programma non conosce prima dell'allocazione e dipende dall'input. Ad esempio, il tuo programma ha bisogno di leggere un file immagine. Quanto è grande quel file di immagine? Potremmo dire abbiamo memorizzare in un array come questo:

unsigned char data[1000000]; 

Ma che funzionerebbe solo se la dimensione dell'immagine è inferiore o uguale a 1000000 byte, e sarebbe anche uno spreco per immagini più piccole.Invece, siamo in grado di allocare dinamicamente la memoria:

unsigned char* data = new unsigned char[file_size]; 

Qui, file_size è determinata in fase di esecuzione. Non è possibile indicare questo valore al momento della compilazione.

+1

Non si può semplicemente: "unsigned char data [file_size];"? – user1066113

+0

No, non è possibile. Bene, alcuni compilatori C potrebbero accettarlo, ma questa è ancora un'allocazione dinamica e non standard C (89) né C++; vedi [VLA] (http://en.wikipedia.org/wiki/Variable-length_array). ISO C99 supporta VLA. –

2

Ogniqualvolta si utilizza new in C++, la memoria viene allocata tramite malloc che chiama la chiamata di sistema sbrk (o simile) stessa. Pertanto nessuno, ad eccezione del sistema operativo, ha conoscenza della dimensione richiesta. Dovrai quindi utilizzare delete (che chiama free che va di nuovo a sbrk) per ridare memoria al sistema. Altrimenti si verificherà una perdita di memoria.

Ora, quando si tratta del secondo caso, il compilatore conosce la dimensione della memoria allocata. Cioè, nel tuo caso, la dimensione di uno int. L'impostazione di un puntatore all'indirizzo di questo int non modifica nulla nella conoscenza della memoria necessaria. O con altre parole: il compilatore è in grado di occuparsi della liberazione della memoria. Nel primo caso con new questo non è possibile.

In aggiunta a quello: new rispettivamente malloc non è necessario allocare esattamente la dimensione requsted, il che rende le cose un po 'più complicate.

Modifica

più due frasi comuni: Il primo caso è noto anche come allocazione memoria statica (fatto dal compilatore), secondo caso si riferisce ad allocazione dinamica della memoria (fatto dal sistema di esecuzione).

2

Per saperne di più dynamic memory allocation e anche garbage collection

Hai davvero bisogno di leggere un buon C o C++ libro di programmazione.

Spiegare in dettaglio richiederebbe molto tempo.

Il cumulo è la memoria all'interno del quale l'allocazione dinamica (con new in C++ o malloc in C) avviene. Ci sono system calls coinvolti nella crescita e nella riduzione dell'heap. Su Linux, sono mmap & munmap (utilizzato per implementare malloc e new ecc ...).

È possibile chiamare molte volte l'allocazione primitiva. Quindi puoi inserire int *p = new int; all'interno di un ciclo e ottenere una nuova posizione ogni volta che fai un ciclo!

Non dimenticare di rilasciare memoria (con delete in C++ o free in C). Altrimenti, riceverai un memory leak, un tipo di bug cattivo. Su Linux, valgrind aiuta a catturarli.

1

In breve, la durata dell'oggetto dell'oggetto dinamicamente allocata è controllata dall'utente e non dalla lingua. Questo ti permette di lasciarlo vivo finchè è richiesto (al contrario della fine dello scope), possibilmente determinato da una condizione che può essere calcolata solo a run-rime.

Inoltre, la memoria dinamica è in genere molto più "scalabile", ovvero è possibile allocare più oggetti e/o più grandi rispetto all'allocazione basata su stack.

L'allocazione essenzialmente "contrassegna" un pezzo di memoria in modo che nessun altro oggetto possa essere allocato nello stesso spazio. La disallocazione "designa" quel pezzo di memoria in modo che possa essere riutilizzato per allocazioni successive. Se non si riesce ad deallocare la memoria dopo che non è più necessaria, si ottiene una condizione nota come "perdita di memoria" - il programma sta occupando una memoria che non ha più bisogno, portando a un possibile errore nell'assegnare nuova memoria (a causa della mancanza di memoria) e, in generale, sta mettendo a dura prova il sistema.

4
  1. Il programma riceve un blocco iniziale di memoria all'avvio. Questa memoria è denominata stack. Di questi tempi l'importo è in genere di circa 2 MB.

  2. Il programma può richiedere al sistema operativo memoria aggiuntiva. Questa è chiamata allocazione dinamica della memoria. Questo alloca memoria sul negozio gratuito (terminologia C++) o heap (terminologia C). È possibile chiedere la quantità di memoria che il sistema è disposto a fornire (più gigabyte).

La sintassi per allocare una variabile in pila assomiglia a questo:

{ 
    int a; // allocate on the stack 
} // automatic cleanup on scope exit 

La sintassi per allocare una variabile utilizzando memoria dalla memoria libera simile a questo:

int * a = new int; // ask OS memory for storing an int 
delete a; // user is responsible for deleting the object 


Per rispondere alle tue domande:

Quando è preferito un metodo all'altro.

  1. Generalmente è preferibile l'assegnazione dello stack.
  2. Allocazione dinamica richiesta quando è necessario memorizzare un oggetto polimorfico utilizzando il suo tipo di base.
  3. Usare sempre puntatore intelligente per automatizzare l'eliminazione:
    • C++ 03: boost::scoped_ptr, boost::shared_ptr o std::auto_ptr.
    • C++ 11: std::unique_ptr o std::shared_ptr.

Ad esempio:

// stack allocation (safe) 
Circle c; 

// heap allocation (unsafe) 
Shape * shape = new Circle; 
delete shape; 

// heap allocation with smart pointers (safe) 
std::unique_ptr<Shape> shape(new Circle); 

ulteriormente più il motivo per cui ho bisogno di liberare la memoria nel primo caso, ma non nel secondo caso.

Come accennato in precedenza, le variabili assegnate allo stack vengono automaticamente deallocate all'uscita dell'ambito. Si noti che non è consentito eliminare la memoria di stack. In questo modo si danneggerebbe inevitabilmente la tua applicazione.

+0

Questa risposta è molto meglio delle altre perché in realtà spiega quale allocazione di memoria usare in quale situazione –

Problemi correlati