In che modo delete [] "conosce" la dimensione dell'array operando?


250
Foo* set = new Foo[100];
// ...
delete [] set;

Non si superano i limiti dell'array delete[]. Ma dove sono archiviate queste informazioni? È standardizzato?


sourceforge.net/projects/fastmm è open source e sostituisce il gestore della memoria. Qui puoi scoprire come funziona la gestione della memoria e da dove provengono le informazioni per allocare ed eliminare le memorie.

1
Notare che FastMM è specifico solo per i compilatori Delphi / C ++ Builder, non è un gestore di memoria generico per C ++. Non è nemmeno scritto in C ++.
Remy Lebeau,

Risposte:


181

Quando si alloca memoria sull'heap, l'allocatore terrà traccia di quanta memoria è stata allocata. Questo è di solito memorizzato in un segmento "head" appena prima della memoria che viene allocata. In questo modo quando è il momento di liberare la memoria, il de-allocatore sa esattamente quanta memoria liberare.


4
Si noti che questo vale solo per le allocazioni di array in C ++. Tutte le altre allocazioni si basano sulla dimensione del tipo. Alcune librerie memorizzano tutte le dimensioni di allocazione, di solito solo in modalità di debug.
Zan Lynx,

97
Non vi è assolutamente alcun motivo per cui queste informazioni non debbano essere disponibili per il programmatore. Posso passare un puntatore a una funzione e liberarla, ma per ottenere la dimensione da solo in quella stessa funzione devo passare un parametro aggiuntivo. Ha senso?
Mark Ruzon,

26
@Mark, ha una piccola quantità di senso, perché in teoria libera l'allocatore di memorizzare sempre la dimensione del blocco allocato (che può differire dalla dimensione del blocco richiesto ). Alcuni progetti di allocatori potrebbero aver bisogno di queste informazioni per i propri scopi o potrebbero non essere abbastanza sofisticati da utilizzare le informazioni sui tipi per tenere traccia delle dimensioni delle allocazioni di heap non array, ecc. Costringere l'allocatore a memorizzare le dimensioni richieste (in modo da non è necessario passare da soli le dimensioni dell'array) potrebbe essere un piccolo ingombro, ma potrebbe avere un impatto sulle prestazioni su progetti di allocatori concepibili.
Doug McClean,

33
Siamo spiacenti, ma questa risposta non è pertinente. Quello che QuantumPete ha descritto è fondamentalmente "Come freesa quanta memoria da deallocare". Sì, la dimensione del blocco di memoria viene memorizzata "da qualche parte" malloc(normalmente nel blocco stesso), quindi è così che freesa. Tuttavia, new[]/ delete[]è una storia diversa. Quest'ultimo funziona fondamentalmente su malloc/ free. new[]memorizza anche il numero di elementi che ha creato nel blocco di memoria (indipendentemente da malloc), in modo che in seguito delete[]possa recuperare e utilizzare quel numero per chiamare il numero corretto di distruttori.
AnT

26
Cioè fisicamente due contatori sono memorizzati nel blocco: dimensione del blocco (per malloc) e conteggio degli elementi (per new[]). Si noti che il primo non può essere utilizzato per calcolare il secondo, poiché in generale la dimensione del blocco di memoria può essere maggiore del necessario per l'array della dimensione richiesta. Si noti inoltre che il contatore degli elementi dell'array è necessario solo per i tipi con distruttore non banale. Per i tipi con banale distruttore il contatore non viene memorizzato da new[]e, ovviamente, non recuperato da delete[].
AnT

23

Uno degli approcci per i compilatori è quello di allocare un po 'più di memoria e memorizzare un conteggio di elementi in un elemento head.

Esempio come potrebbe essere fatto:

Qui

int* i = new int[4];

il compilatore assegnerà i sizeof(int)*5byte.

int *temp = malloc(sizeof(int)*5)

Memorizzerà "4" nei primi sizeof(int)byte

*temp = 4;

e impostare i

i = temp + 1;

Quindi ipunta a una matrice di 4 elementi, non 5.

E cancellazione

delete[] i;

verrà elaborato nel modo seguente:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)

9

Le informazioni non sono standardizzate. Tuttavia nelle piattaforme su cui ho lavorato queste informazioni sono archiviate in memoria appena prima del primo elemento. Pertanto si potrebbe teoricamente accedervi e ispezionarlo, tuttavia non ne vale la pena.

Anche per questo è necessario utilizzare delete [] quando è stata allocata memoria con new [], poiché la versione array di delete sa che (e dove) deve cercare di liberare la giusta quantità di memoria e chiamare il numero appropriato di distruttori per gli oggetti.


5

Fondamentalmente è organizzato in memoria come:

[info] [mem che hai chiesto ...]

Dove info è la struttura utilizzata dal compilatore per archiviare la quantità di memoria allocata e cosa no.

Questo dipende dall'implementazione però.


4

Questo non è qualcosa che è nelle specifiche - dipende dall'implementazione.


3

È definito nello standard C ++ per essere specifico del compilatore. Il che significa magia del compilatore. Può rompersi con restrizioni di allineamento non banali su almeno una piattaforma principale.

Puoi pensare a possibili implementazioni realizzando che delete[]è definito solo per i puntatori restituiti da new[], che potrebbe non essere lo stesso puntatore restituito da operator new[]. Un'implementazione allo stato brado è quella di memorizzare il conteggio degli array nel primo int restituito da operator new[]e new[]restituire un offset del puntatore oltre quello. (Ecco perché gli allineamenti non banali possono rompersi new[].)

Tieni presente che operator new[]/operator delete[]! = new[]/delete[].

Inoltre, questo è ortogonale al modo in cui C conosce la dimensione della memoria allocata da malloc.


2

Perché l'array da "cancellare" avrebbe dovuto essere creato con un singolo utilizzo del "nuovo" operatore. La "nuova" operazione avrebbe dovuto mettere tali informazioni nell'heap. Altrimenti, come potrebbero gli usi aggiuntivi di new sapere dove finisce l'heap?


0

Non è standardizzato. Nel runtime di Microsoft il nuovo operatore utilizza malloc () e l'operatore delete utilizza free (). Quindi, in questa impostazione la tua domanda è equivalente alla seguente: Come fa free () a conoscere la dimensione del blocco?

C'è un po 'di contabilità in corso dietro le quinte, vale a dire in fase di esecuzione.


5
Non vero. Prima di chiamare gratis, delete [] deve prima chiamare i distruttori. Per questo, la dimensione totale delle allocazioni non è sufficiente. In realtà new [] ed delete [] funzionano in modo diverso per tipi normali e distrutti in VC ++.
Suma,

0

Questo è un problema più interessante di quanto potresti pensare all'inizio. Questa risposta riguarda una possibile implementazione.

In primo luogo, mentre a un certo livello il tuo sistema deve sapere come "liberare" il blocco di memoria, il malloc / free sottostante (che generalmente chiama new / delete / new [] / delete []) non ricorda sempre esattamente quanta memoria se lo chiedi, può essere arrotondato per eccesso (ad esempio, una volta sopra 4K è spesso arrotondato per eccesso al blocco di dimensioni 4K successivo).

Pertanto, anche se è possibile ottenere la dimensione del blocco di memoria, ciò non ci dice quanti valori ci sono nella nuova memoria [], in quanto può essere più piccola. Pertanto, dobbiamo memorizzare un numero intero in più che ci dica quanti valori ci sono.

SALVO, se il tipo in costruzione non ha un distruttore, allora delete [] non deve fare altro che liberare il blocco di memoria, e quindi non deve memorizzare nulla!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.