2012-03-05 10 views
21

Consideriamo questo brevissimo frammento di codice:Posso supporre che chiamare realloc con una dimensione più piccola libererà il resto?

#include <stdlib.h> 

int main() 
{ 
    char* a = malloc(20000); 
    char* b = realloc(a, 5); 

    free(b); 
    return 0; 
} 

Dopo aver letto la pagina man di realloc, non ero del tutto sicuro che la seconda riga causerebbe le 19995 byte in più per essere liberati. Per citare la pagina man: The realloc() function changes the size of the memory block pointed to by ptr to size bytes., ma da quella definizione, posso essere sicuro che il resto verrà liberato?

Voglio dire, il blocco puntato da b contiene certamente 5 byte liberi, quindi sarebbe sufficiente per un allocatore pigro che non fa semplicemente nulla per la linea realloc?

Nota: L'allocatore che uso sembra liberare i 19 995 byte in più, come dimostrato da valgrind quando commentando la linea free(b):

==4457== HEAP SUMMARY: 
==4457==  in use at exit: 5 bytes in 1 blocks 
==4457== total heap usage: 2 allocs, 1 frees, 20,005 bytes allocated 
+0

L'intestazione '' non è definita dallo standard: preferisce usare ''. Anche il cast del valore restituito da 'malloc' (o' realloc') non ha scopo utile e può nascondere un errore (* rappresentazione di 'void *' e 'int' che sono diversi *) il compilatore avrebbe preso altrimenti. – pmg

+1

["La dimensione del blocco di memoria indicata dal parametro ptr viene modificata in byte di dimensione, espandendo o riducendo la quantità di memoria disponibile nel blocco."] (Http://www.cplusplus.com/reference/clibrary/cstdlib/realloc /) –

+0

@pmg ok Non lo sapevo. Cambierò nel mio snippet – qdii

risposta

19

Sì, garantito dallo standard C se è possibile assegnare il nuovo oggetto.

(C99, 7.20.3.4p2) "La funzione realloc rilascia il vecchio oggetto puntato da ptr e restituisce un puntatore a un nuovo oggetto con le dimensioni specificate per dimensione."

+0

[La bozza C99] (http://people.freebsd.org/~green/c9x-draft.txt) non menziona mai niente contro realloc che non fa nulla quando la vecchia dimensione è uguale alla nuova. Immagino che tutte le implementazioni lo violino? – qdii

+3

C99 7.20.3.4 §4: * La funzione realloc restituisce un puntatore al nuovo oggetto (che può avere lo stesso valore di un puntatore al vecchio oggetto) * – Christoph

15

Sì — se riesce.

tuo frammento di codice mostra un noto, nefasto errore:

char* b = (char*) realloc(a, 5); 

In caso positivo, la memoria precedentemente allocato a a verrà liberata, e b punterà 5 byte di memoria che possono o non può sovrapporsi al blocco originale.

Tuttavia, se la chiamata non riesce, b sarà null, ma a sarà ancora puntare alla sua memoria originale, che sarà ancora valido. In tal caso, è necessario free(a) per rilasciare la memoria.

E 'ancora peggio se si utilizza il comune (pericoloso) idioma:

a = realloc(a, NEW_SIZE);  // Don't do this! 

Se la chiamata a realloc non riesce, una sarà null e la sua memoria originale saranno orfani, che lo rendono irrimediabilmente perduto fino a quando il programma di uscite.

+0

Hai ragione. Avevo visto cppcheck lamentarsi di questo. Immagino che questo sia un killer per i test d'esame, ma davvero, mai, mai? – qdii

+0

@qdii: nel mondo embedded tutte le scommesse sono disattivate; inoltre, il sistema operativo potrebbe imporre alcuni limiti, ad esempio sullo spazio di memoria virtuale tramite 'ulimit' – Christoph

+0

Sì, questo succede davvero nel mondo reale. Perché pensi che così tanto schifoso software si blocca quando si esaurisce la memoria (su un sistema senza scambio) piuttosto che dare un messaggio che non può eseguire l'operazione richiesta a causa della mancanza di memoria? –

2

Questo dipende dall'implementazione della libc. Tutti i seguenti è un comportamento conforme:

  • non fare nulla, cioè lasciando che i dati rimangono dove si trova e la restituzione del vecchio blocco, forse ri-utilizzando i byte ora non utilizzate per ulteriori accantonamenti (afaik tale riutilizzo è non comune)
  • copiare i dati su un nuovo blocco e rilasciando il vecchio indietro al sistema operativo
  • copiare i dati su un nuovo blocco e la conservazione del vecchio per ulteriori assegnazioni

e 'anche possibile a

  • ritorno un puntatore nullo se un nuovo blocco non può essere allocato

In questo caso, i vecchi dati saranno inoltre rimangono dove è, che può portare a perdite di memoria, ad esempio se il ritorno il valore di realloc() sovrascrive l'unica copia del puntatore su quel blocco.

Un'implementazione ragionevole della libc utilizzerà un po 'di euristica per determinare quale soluzione sia la più efficiente.

Ricorda inoltre che questa descrizione è a livello di implementazione: semanticamente, realloc() libera sempre l'oggetto finché l'allocazione non fallisce.

+1

Per quanto riguarda "afaik tale riutilizzo non è comune", non ho mai sentito di un'implementazione del mondo reale che lasci sul posto la vecchia allocazione e non liberi la coda per il riutilizzo. Questo sarebbe patologicamente cattivo, anche se legale, comportamento. –

+0

Ci sono degli standard che definiscono questo comportamento o sono davvero all'implementazione di libc? –

+1

@ rr-: lo standard C e POSIX lo lasciano all'implementazione; Non sono a conoscenza di alcuna specifica che definisce il comportamento, ma potrebbe essere documentata nel manuale libc – Christoph

0

Sembra improbabile che 19995 byte siano stati liberati. Ciò che è più probabile è che realloc ha sostituito il blocco da 20000 byte con un altro blocco da 5 byte, , ovvero il blocco da 20000 byte è stato liberato e un nuovo blocco da 5 byte è stato allocato.

1

La funzione realloc ha il seguente contratto:

void * result = realloc (PTR, new_size)

  • Se il risultato è NULL, allora PTR è ancora valida e immutata.
  • Se il risultato è diverso da NULL, quindi ptr non è più valido (come se fosse stato liberato) e non deve essere mai più utilizzato. risultato è ora un puntatore a esattamente new_size byte di dati.

I precisi dettagli di ciò che accade sotto il cofano sono specifiche per l'attuazione - per un risultato ad esempio può essere uguale PTR (ma lo spazio aggiuntivo oltre new_size non deve essere toccato più) e realloc può chiamare gratis, oppure può fare la sua libera rappresentazione interna. La cosa fondamentale è che come sviluppatore non si ha più la responsabilità di ptr se realloc restituisce un valore non nullo e se ne ha ancora la responsabilità se realloc restituisce NULL.

+0

Inoltre, il passaggio new_size = 0 è lo stesso di free (ptr) e restituisce NULL. Questo sarebbe il caso in cui risultato == NULL e ptr non sono più validi. – gnasher729