Come fa il libero a sapere quanto liberare?


385

Nella programmazione C, puoi passare qualsiasi tipo di puntatore che ti piace come argomento da liberare, come fa a sapere la dimensione della memoria allocata da liberare? Ogni volta che passo un puntatore a una funzione, devo anche passare la dimensione (cioè una matrice di 10 elementi deve ricevere 10 come parametro per conoscere la dimensione della matrice), ma non devo passare la dimensione alla funzione gratuita. Perché no, e posso usare questa stessa tecnica nelle mie funzioni per salvarmi dalla necessità di aggirare la variabile extra della lunghezza dell'array?


Una domanda simile: stackoverflow.com/questions/851958/… (anche se direi che non è del tutto duplicato)
John Carter,

Il sistema amico è un altro modo per farlo che può capire in base al puntatore, senza sovraccarico in ogni blocco.
EvilTeach

Risposte:


349

Quando si chiama malloc(), si specifica la quantità di memoria da allocare. La quantità di memoria effettivamente utilizzata è leggermente superiore a questa e include informazioni aggiuntive che registrano (almeno) quanto è grande il blocco. Non è possibile (in modo affidabile) accedere a tali altre informazioni - e nemmeno dovresti :-).

Quando chiami free(), guarda semplicemente le informazioni extra per scoprire quanto è grande il blocco.


44
Cordiali saluti, ad esempio BSD deve malloc_size()accedere in modo affidabile alla dimensione del blocco da un malloc()puntatore ed. Ma non esiste un modo affidabile e portatile.
laalto,

50
Penso che sia importante dire che questo blocco di informazioni extra si trova prima del puntatore restituito.
Georg Schölly,

39
@gs Beh, dipende dall'implementazione. Ma sì, è lì che è di solito.
Falaina,

31
Riesci a immaginare l'orrore se free()richiesto al programmatore di riferire accuratamente quanto era grande il malloc()blocco? Le perdite di memoria sono già abbastanza gravi.
MusiGenesis,

35
Perché tali informazioni sono disponibili per malloc()e free(), ma è necessario memorizzare le dimensioni di un array? Perché non consentirebbero di fare qualcosa come blockSize(ptr)se stessero comunque memorizzando le informazioni?
corsiKa

144

La maggior parte delle implementazioni delle funzioni di allocazione della memoria C memorizzerà le informazioni di contabilità per ciascun blocco, sia in linea che separatamente.

Un modo tipico (in linea) è di allocare effettivamente sia un'intestazione che la memoria richiesta, riempiti con una dimensione minima. Ad esempio, se hai richiesto 20 byte, il sistema potrebbe allocare un blocco di 48 byte:

  • Intestazione a 16 byte contenente dimensioni, marker speciale, checksum, puntatori al blocco successivo / precedente e così via.
  • Area dati di 32 byte (i 20 byte completati fino a un multiplo di 16).

L'indirizzo che ti è stato dato è l'indirizzo dell'area dati. Quindi, quando libererai il blocco, freeprenderai semplicemente l'indirizzo che gli fornisci e, supponendo che tu non abbia riempito quell'indirizzo o la memoria che lo circonda, controlla le informazioni contabili immediatamente prima di esso. Graficamente, ciò sarebbe sulla falsariga di:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Tieni presente che le dimensioni dell'intestazione e del padding sono totalmente definite dall'implementazione (in realtà, l'intera cosa è definita dall'implementazione (a) ma l'opzione di contabilità in linea è comune).

I checksum e i marcatori speciali presenti nelle informazioni contabili sono spesso la causa di errori come "Arena di memoria danneggiata" o "Doppio libero" se li sovrascrivi o li liberi due volte.

Il padding (per rendere più efficiente l'allocazione) è il motivo per cui a volte puoi scrivere un po 'oltre la fine dello spazio richiesto senza causare problemi (tuttavia, non farlo, è un comportamento indefinito e, solo perché a volte funziona, non lo fa' voglio dire che va bene farlo).


(a) Ho scritto implementazioni di mallocin sistemi embedded in cui hai ottenuto 128 byte indipendentemente da ciò che hai richiesto (che era la dimensione della struttura più grande nel sistema), supponendo che tu abbia richiesto 128 byte o meno (richieste di più essere soddisfatto con un valore di ritorno NULL). È stata utilizzata una maschera di bit molto semplice (cioè non in linea) per decidere se un blocco di 128 byte è stato allocato o meno.

Altri che ho sviluppato avevano pool diversi per blocchi da 16 byte, blocchi da 64 byte, blocchi da 256 byte e blocchi da 1K, usando nuovamente una maschera di bit per decidere quali blocchi sono stati utilizzati o disponibili.

Entrambe queste opzioni sono riuscite a ridurre il sovraccarico delle informazioni contabili e ad aumentare la velocità malloce free(non è necessario unire i blocchi adiacenti durante la liberazione), particolarmente importante nell'ambiente in cui lavoravamo.


@paxdiablo Significa che malloc non alloca blocchi contigui di memoria?
user10678,

2
@ user10678, l'unico vero requisito mallocè che ti dia, per il caso riuscito, un blocco di memoria almeno grande quanto quello che hai chiesto. I singoli blocchi sono contigui in termini di come accedi agli elementi al loro interno, ma non è necessario che le arene da cui provengono i blocchi siano contigue.
paxdiablo,

Domanda correlata: perché non esiste alcuna variazione di malloc / free, in cui si specifica la dimensione durante la liberazione e quindi non è necessario memorizzarla?
user253751

@ user253751, perché poi di theer una più cosa che dovete tenere traccia di, oltre al puntatore stesso. E 'sia inutile e pericoloso: void *x = malloc(200); free(x, 500);è non andare a finire bene :-) In ogni caso, per l'efficienza, l' effettiva dimensione del buffer potrebbe essere più grande (non si può fare affidamento su questo).
paxdiablo,

@paxdiablo Evita inoltre di sprecare memoria per contenere le dimensioni.
user253751

47

Dall'elenco delle comp.lang.cdomande frequenti: in che modo gratuito sa quanti byte liberare?

L'implementazione malloc / free ricorda la dimensione di ciascun blocco quando viene allocata, quindi non è necessario ricordargli la dimensione durante la liberazione. (In genere, la dimensione viene memorizzata accanto al blocco allocato, motivo per cui le cose di solito si rompono male se i limiti del blocco allocato sono anche leggermente superati)


2
Questa è una non risposta. La domanda è esattamente questa: perché il libero può cercare in modo affidabile la dimensione del blocco, ma non c'è nessuna funzione disponibile per il programmatore che lo fa?
Bananach,

Questo è davvero un dettaglio di implementazione per l'APP malloc e non esiste un'API per recuperare queste informazioni in modo standard (per quanto ne sappia). Il "sistema" lo registra e lo utilizza su free. Forse la risposta non ti soddisfa, ma non credo che ne otterrai uno con informazioni più genericamente applicabili :-)
jdehaan,

6

Questa risposta è stata trasferita da Come fa free () a sapere quanta memoria da deallocare? dove mi fu bruscamente impedito di rispondere da una domanda apparentemente duplicata. Questa risposta dovrebbe quindi essere rilevante per questo duplicato:


Nel caso di malloc, l'allocatore di heap memorizza una mappatura del puntatore restituito originale, ai dettagli rilevanti necessari per freela memoria in un secondo momento. Ciò comporta in genere l'archiviazione della dimensione della regione di memoria in qualsiasi forma rilevante per l'allocatore in uso, ad esempio la dimensione non elaborata o un nodo in un albero binario utilizzato per tenere traccia delle allocazioni o un conteggio delle "unità" di memoria in uso.

freenon fallirà se si "rinomina" il puntatore o lo si duplica in alcun modo. Tuttavia, non viene conteggiato il riferimento e solo il primo freesarà corretto. Gli altri freesono errori "double free".

Tentativo di freequalsiasi puntatore con un valore diverso da quelli restituiti da precedenti mallocs, e finora non concordato è un errore. Non è possibile liberare parzialmente le aree di memoria restituite da malloc.


Ho cambiato il valore di un puntatore restituito da una chiamata malloc. E l'ho liberato senza errori. Perché? Vedi qui: stackoverflow.com/questions/42618390/...
smwikipedia

4

Su una nota correlata la libreria GLib ha funzioni di allocazione della memoria che non salvano le dimensioni implicite - e quindi si passa semplicemente il parametro size a libero. Questo può eliminare parte dell'overhead.


3

malloc()e free()dipendono dal sistema / compilatore, quindi è difficile dare una risposta specifica.

Maggiori informazioni su questa altra domanda .


2
Sono veramente dipendenti dalla libreria (in genere la libreria C, che di solito è strettamente legata al sistema operativo). Per il compilatore, sono solo funzioni.
Donal Fellows,

2

Il gestore heap ha archiviato la quantità di memoria appartenente al blocco allocato da qualche parte quando è stato chiamato malloc .

Non ne ho mai implementato uno da solo, ma immagino che la memoria proprio di fronte al blocco allocato potrebbe contenere le meta informazioni.


3
Questa è un'implementazione possibile, ma si potrebbe escogitare un sistema in cui tutta la memoria è tracciata in una singola tabella in una pagina totalmente diversa, non necessariamente in nessun posto vicino al pool di memoria da cui viene allocato.
effimero

2

La tecnica originale era allocare un blocco leggermente più grande e memorizzare le dimensioni all'inizio, quindi dare all'applicazione il resto del blog. Lo spazio extra ha una dimensione e può essere collegato per unire i blocchi liberi per riutilizzarli.

Ci sono alcuni problemi con questi trucchi, tuttavia, come la cattiva gestione della memoria e della cache. L'uso della memoria nel blocco tende a sfogliare inutilmente le cose e crea anche pagine sporche che complicano la condivisione e la copia su scrittura.

Quindi una tecnica più avanzata è quella di mantenere una directory separata. Sono stati inoltre sviluppati approcci esotici in cui le aree della memoria usano la stessa potenza di due dimensioni.

In generale, la risposta è: una struttura di dati separata è allocata per mantenere lo stato.


1

Per rispondere alla seconda metà della domanda: sì, puoi, e un modello abbastanza comune in C è il seguente:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;

Questa è una tecnica completamente diversa da quella usata da BSD malloc per piccoli oggetti (anche se è una tecnica perfettamente valida per creare array in stile Pascal)
Pete Kirkham,

0

Quando chiamiamo malloc consuma semplicemente più byte dal suo requisito. Questo maggiore consumo di byte contiene informazioni come somma di controllo, dimensioni e altre informazioni aggiuntive. Quando chiamiamo gratis in quel momento, vanno direttamente a quelle informazioni aggiuntive in cui trova l'indirizzo e trova anche quanto blocco sarà gratuito.


0

per rispondere alla seconda domanda, sì, è possibile (in un certo senso) utilizzare la stessa tecnica malloc() assegnando semplicemente la prima cella all'interno di ogni array alle dimensioni dell'array. che consente di inviare l'array senza inviare un argomento di dimensioni aggiuntive.

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.