2015-02-22 13 views
21

Ho trovato che alcune persone e riferimenti come libri affermano che se le origini p != NULL e p dall'assegnazione precedente (ad esempio da malloc), quindi realloc(p, 0) è equivalente a free(p) su GNU/Linux. Per supportare questa tesi man realloc stati esattamente in quel modo (sottolineatura mia andando avanti):Realloc (p, 0) implica veramente free (p) in glibc?

La funzione realloc() modifica la dimensione del blocco di memoria puntato ptr a size byte. Il contenuto sarà invariato nell'intervallo da all'inizio della regione fino al minimo delle vecchie e nuove dimensioni. Se la nuova dimensione è più grande della vecchia dimensione, la memoria aggiunta non sarà inizializzata. Se ptr è NULL, la chiamata è equivalente a malloc (dimensione), per tutti i valori di dimensione; se la dimensione è uguale a zero e ptr non è NULL, quindi la chiamata è equivalente a free (ptr). A meno che ptr sia NULL, deve essere stato restituito da una chiamata precedente a malloc(), calloc() o realloc(). Se l'area puntata a è stata spostata, viene eseguito uno libero (ptr).

come si possono trovare nel this question, il C standard non definisce con precisione ciò che dovrebbe accadere e il comportamento effettivo è definito dall'implementazione. Più in particolare:

Il C11 §7.22.3/P1 funzioni di gestione della memoria dice:

Se la dimensione dello spazio richiesto è pari a zero, il comportamento è implementazione definita: sia un null il puntatore viene restituito o il comportamento è come se la dimensione fosse un valore diverso da zero, con la differenza che il puntatore restituito non deve essere utilizzato per accedere a un oggetto.

e C11 §7.22.3.5 La funzione realloc contiene:

3) (...) Se la memoria per il nuovo oggetto non può essere allocato, il vecchio oggetto è non deallocato e il suo valore non è stato modificato.

4) La funzione realloc restituisce un puntatore al nuovo oggetto (che può avere lo stesso valore di un puntatore al vecchio oggetto), o un puntatore nullo se il nuovo oggetto non può essere allocata.

ho scritto qualche codice di base per scoprire il comportamento effettivo con l'aiuto di mcheck, di controllo della memoria, che è fornito con glibc:

#include <mcheck.h> 
#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    int a = 5; 
    int *p, *q; 

    mtrace(); 

    p = malloc(sizeof(int)); 
    q = &a; 

    printf("%p\n", (void *) p); 
    printf("%p\n", (void *) q); 

    q = realloc(p, 0); 

    printf("%p\n", (void *) p); 
    printf("%p\n", (void *) q); 

    return 0; 
} 

ed i risultati sono:

$ gcc -g check.c 
$ export MALLOC_TRACE=report 
$ ./a.out 
0xfd3460 
0x7ffffbc955cc 
0xfd3460 
(nil) 
[[email protected] workspace]$ mtrace a.out report 

Memory not freed: 
----------------- 
      Address  Size  Caller 
0x0000000000fd3460  0x4 at /home/grzegorz/workspace/check.c:12 

Come si potrebbe vedere q impostato su NULL. Sembra che lo free() non sia stato davvero chiamato. In realtà non può essere se non la mia interpretazione è corretta: dal realloc è tornato NULL puntatore, il nuovo oggetto non avrebbe potuto essere assegnato, il che implica che:

il vecchio oggetto non viene deallocato e il suo valore è invariato

È corretto?

+11

La dicitura relativa a 'realloc (ptr, 0)' equivalente a 'free (ptr)' era in POSIX, ma il [riferimento POSIX reale »(http: //pubs.opengroup. org/onlinepubs/9699919799/functions/realloc.html) ora utilizza la lingua dallo standard C. Lo standard POSIX tuttavia dice "se' realloc() 'restituisce un puntatore nullo, lo spazio puntato da p non è stato liberato". –

risposta

9

Edit: Il tuo glibc sembra essere un pre-2.18, in 2.18 un bug è stato risolto in mtrace (vedi here). Con un rapporto di 2.20 glibc il programma di test riporta: "Nessuna perdita di memoria".

free è chiamato in glibc. Dalle fonti di glibc corrente 2.21 (here e here):

/* 
    REALLOC_ZERO_BYTES_FREES should be set if a call to 
    realloc with zero bytes should be the same as a call to free. 
    This is required by the C standard. Otherwise, since this malloc 
    returns a unique pointer for malloc(0), so does realloc(p, 0). 
*/ 

#ifndef REALLOC_ZERO_BYTES_FREES 
#define REALLOC_ZERO_BYTES_FREES 1 
#endif 

void * 
__libc_realloc (void *oldmem, size_t bytes) 
{ 
    mstate ar_ptr; 
    INTERNAL_SIZE_T nb;   /* padded request size */ 

    void *newp;    /* chunk to return */ 

    void *(*hook) (void *, size_t, const void *) = 
    atomic_forced_read (__realloc_hook); 
    if (__builtin_expect (hook != NULL, 0)) 
    return (*hook)(oldmem, bytes, RETURN_ADDRESS (0)); 

#if REALLOC_ZERO_BYTES_FREES 
    if (bytes == 0 && oldmem != NULL) 
    { 
     __libc_free (oldmem); return 0; 
    } 
#endif 
+0

Questo non esclude alcune intestazione (forse facoltativamente) che definiscono "REALLOC_ZERO_BYTES_FREES" come zero. Nessuna intestazione lo sta facendo, ma penso che dovresti dirlo nella tua risposta. – hvd

+1

@hvd La definizione di 'REALLOC_ZERO_BYTES_FREES' ha effetto solo quando si * costruisce * glibc. Non ha alcun effetto quando si utilizza solo la libreria. –

+0

@ 一 二三 Bene duh. :) Mi riferisco alle intestazioni che glibc sta usando da sola. Questo non è il file sorgente completo. Il file sorgente completo contiene le direttive '# include'. – hvd

2

Poiché il comportamento di realloc() quando viene passata una dimensione di 0 è definito implenentation ...

Se la dimensione dello spazio richiesto è nullo, il comportamento è definito dall'implementazione: o un puntatore nullo è restituito, o il comportamento è come se la dimensione fosse un valore diverso da zero, tranne che il puntatore restituito non deve essere utilizzato per accedere a un oggetto.

... l'equivalente portatile per

void * p = malloc(1); 
free(p); 

deve essere

void * p = malloc(1); 
p = realloc(p, 0) 
free(p); /* because of the part after the "or" as quoted above. 

Il saldo memoria dovrebbe essere anche successivamente.

aggiornamento che copre realloc() 's caso di "errore":

void * p = malloc(1); 
{ 
    void * q = realloc(p, 0); 
    p = q ?q :p; 
} 
free(p); /* because of the part after the "or" as quoted above. 
+5

Se 'realloc' restituisce un puntatore NULL, hai perso il puntatore che deve essere liberato :( –

+0

Vuoi dire l'equivalente portatile di' void * p = malloc (1); realloc (p, 0); 'giusto? – immibis

+2

@BenVoigt se 'realloc' restituisce un puntatore NULL, non sai se liberare il vecchio puntatore o no! – immibis

2

Date un'occhiata al realloc codice glibc qui: http://code.woboq.org/userspace/glibc/malloc/memusage.c.html#realloc. Si vedrà in linea 434 che free viene chiamato quando la dimensione è 0.

+4

Sei sicuro che questa sia * l'implementazione * di' realloc' di glibc? Trovo le fonti abbastanza confuse e questo file di origine contiene il commento (nel primo line) "L'heap del profilo e l'utilizzo della memoria dello stack del programma in esecuzione." Esiste un altro file, 'malloc/malloc.c', che contiene' __libc_realloc', che è aliasato in 'realloc'. Tale implementazione infatti contiene un compilatore swi tch chiamato 'REALLOC_ZERO_BYTES_FREES' che influenza il comportamento in questo caso. – dyp

+0

Hai ragione. In http://osxr.org/glibc/source/malloc/malloc.c vedere le righe 571--574. Non so quando è impostato o meno ... –

+0

Vedere anche le linee 217-220 in http://www.atnf.csiro.au/computing/software/casacore/casacore-1.2.0/doc/html/ malloc_8h_source.html. La definizione di questi simboli è una questione di coerenza con il comportamento di 'malloc' su allocazioni a dimensione zero. –

7

Anche se la mia intepretazione di "tornato NULL" caso sembra essere corretta (vedere la mia modifica di seguito), glibc sviluppatori decided per mantenerlo coerente con precedente C89 Standard e rifiutato di conformarsi a C99/C11:

Non è possibile che questo venga modificato.Questo è il modo in cui è stato implementato per sempre . C dovrebbe documentare la pratica esistente. Cambiando significherebbe introdurre perdite di memoria.

anche mcheck indicazione era fuorviante, come altri test-case ha dimostrato che la memoria è efficacemente liberato da realloc:

#include <malloc.h> 
#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    int *p, *q; 

    p = malloc(20 * sizeof(int)); 
    malloc_stats(); 

    putchar('\n'); 

    q = realloc(p, 0); 
    malloc_stats(); 

    return 0; 
} 

Qui, l'uscita è:

$ gcc check.c 
$ ./a.out 
Arena 0: 
system bytes  =  135168 
in use bytes  =   96 
Total (incl. mmap): 
system bytes  =  135168 
in use bytes  =   96 
max mmap regions =   0 
max mmap bytes =   0 

Arena 0: 
system bytes  =  135168 
in use bytes  =   0 
Total (incl. mmap): 
system bytes  =  135168 
in use bytes  =   0 
max mmap regions =   0 
max mmap bytes =   0 

EDIT :

Come indicato da hvd in comment, il gruppo di lavoro ISO/IEC ha avuto qualche discussione, materializzata come Defect Report #400. Le modifiche proposte potrebbero probabilmente consentire la pratica esistente di glibc nella revisione futura dello standard C (o eventualmente come rettifica tecnica 1 per C11).

Quello che ho veramente piace di DR # 400 è proposta per:

Aggiungere al comma 7.31.12 un nuovo paragrafo (paragrafo 2):

Invocare realloc con un argomento dimensioni pari a zero è una caratteristica obsoleta.

+2

+1 per aver citato il bug tracker. Ho riaperto il problema da quando la decisione è stata presa dall'ex dittatore di glibc che ora è passato. –

+2

@R ..C'è qualche bug di glibc, però? C modificato per riattivare il comportamento di C90 e glibc se sto leggendo [DR # 400] (http://open-std.org/JTC1/SC22/WG14/www/docs/dr_400.htm) correttamente. O è stato mai accettato? – hvd

+0

@hvd: Suppongo che sarà nel prossimo TC a C11, quindi forse è abbastanza per rendere felici le persone. Penso ancora che sia confuso però. La prima modifica apportata al TC proposto per il DR fa sembrare che NULL sia solo un valore di ritorno valido per 'realloc' quando si è verificato un errore. –