Qual è il punto di malloc (0)?


Risposte:


121

Secondo le specifiche, malloc (0) restituirà "un puntatore nullo o un puntatore univoco che può essere passato con successo a free ()".

Questo fondamentalmente non ti consente di allocare nulla, ma comunque di passare la variabile "artist" a una chiamata a free () senza preoccupazioni. Per scopi pratici, è più o meno come fare:

artist = NULL;

51
Personalmente, penso che l'impostazione su NULL sia una migliore strategia multipiattaforma, poiché free () è garantito (dalle specifiche) per funzionare bene su NULL come input.
Reed Copsey

2
Come menzionato da C. Ross, alcune piattaforme, tecnicamente, potrebbero restituire un puntatore qui (che è un "puntatore univoco che può essere passato a free"), ma se lo stai trattando come un carattere *, potrebbe darti un carattere non valido, non terminato. Potrebbe essere pericoloso fare affidamento su questo in situazioni multipiattaforma.
Reed Copsey

9
Vorrei davvero che anche le specifiche dicessero "passato in sicurezza al riallocazione" -.-
hanshenrik

1
@NSAddict "struttura vuota dove sizeof restituirà 0", per favore fornisci un esempio, suona come un'estensione del linguaggio.
chux - Ripristina Monica il

1
@hanshenrik Chi dice che non puoi? realloc()ti permette di passare qualsiasi puntatore valido restituito da malloc(). Dovrebbe bastare.
glglgl

51

Lo standard C (C17 7.22.3 / 1) dice:

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

Quindi, malloc(0)potrebbe restituire NULLo un puntatore valido che potrebbe non essere dereferenziato . In entrambi i casi, è perfettamente valido chiamarefree() .

Non penso davvero che malloc(0)sia molto utile, tranne nei casi in cui malloc(n)viene chiamato in un ciclo per esempio, en potrebbe essere zero.

Guardando il codice nel link, credo che l'autore avesse due idee sbagliate:

  • malloc(0)restituisce un puntatore valido sempre , e
  • free(0) è cattivo.

Quindi, si è assicurato che quella artiste altre variabili contenessero sempre un valore "valido". Il commento dice molto: // these must always point at malloc'd data.


10
Il fatto che dipenda dall'implementazione lo rende più o meno completamente inutile: questo è uno dei pezzi più schifosi dello standard C, e parecchi membri del comitato degli standard (ad esempio PJ Plauger) si sono lamentati di questo.

10
Sono d'accordo. Se viene malloc(0)restituito un puntatore valido, malloc()restituire NULLsignifica sempre "fallimento" e 0non è più un caso speciale, il che è più coerente.
Alok Singhal,

1
Poiché le circostanze di mallocmancato ottenimento della memoria sono definite dall'implementazione, un'implementazione potrebbe semplicemente definire che le allocazioni di dimensione 0 sono sempre insoddisfacenti ( ENOMEM), e ora malloc(0)restituire 0 (con errno==ENOMEM) è coerente. :-)
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

6
Puoi reallocun puntatore restituito da malloc(0)? Puoi realloc((char*)NULL)?
Braden Best

3
@ Braden Best Sì a entrambi.
chux - Ripristina Monica il

11

Il comportamento di malloc (0) è specifico dell'implementazione. La libreria può restituire NULL o avere il normale comportamento malloc, senza memoria allocata. Qualunque cosa faccia, deve essere documentata da qualche parte.

Di solito, restituisce un puntatore valido e univoco ma NON deve essere dereferenziato. Notare inoltre che PU consumare memoria anche se in realtà non ha allocato nulla.

È possibile riallocare un puntatore malloc (0) non nullo.

Tuttavia, avere un malloc (0) verbatim non è molto utile. Viene utilizzato principalmente quando un'allocazione dinamica è zero byte e non ti importava di convalidarla.


9
malloc()deve conservare le "informazioni di manutenzione" da qualche parte (questa dimensione del blocco allocato, ad esempio, e altri dati ausiliari). Quindi, se malloc(0)non viene restituito NULL, utilizzerà la memoria per memorizzare tali informazioni e, in caso contrario free(), costituirà una perdita di memoria.
Alok Singhal

Le implementazioni di Malloc eseguono la conservazione dei record che potrebbe aggiungere una certa quantità di dati per puntatore restituito oltre alla dimensione richiesta.
user7116

1
La memoria consumata e la memoria allocata non significano la stessa cosa. In questo caso, la maggior parte delle implementazioni restituirà un puntatore univoco. Ciò significa che una parte dello spazio degli indirizzi deve essere sacrificata per quel puntatore. A seconda dell'allocatore, ciò potrebbe effettivamente significare che allocherà 1 byte o più.
Coincoin

1
La libreria può fare quello che vuole - beh, può restituire un puntatore univoco che nessun altro malloc()restituirà, oppure restituire NULL.
Alok Singhal

1
@ jldupont: almeno la libreria di runtime di Microsoft C restituisce un puntatore univoco per malloc(0). Tuttavia, nella stessa implementazione della libreria C standard, realloc(ptr, 0)libera ptre restituisce NULL.
Medinoc

5

C'è una risposta altrove in questa pagina che inizia con "malloc (0) restituirà un indirizzo di memoria valido e il cui intervallo dipenderà dal tipo di puntatore a cui viene allocata la memoria". Questa affermazione non è corretta (non ho abbastanza reputazione per commentare direttamente quella risposta, quindi non posso inserire questo commento direttamente sotto).

L'esecuzione di malloc (0) non allocherà automaticamente la memoria della dimensione corretta. La funzione malloc non sa a cosa stai trasmettendo il risultato. La funzione malloc si basa esclusivamente sul numero di dimensione fornito come argomento. È necessario eseguire malloc (sizeof (int)) per ottenere spazio di archiviazione sufficiente per contenere un int, ad esempio, non 0.


4

malloc(0)non ha alcun senso per me, a meno che il codice non si basi su un comportamento specifico dell'implementazione. Se il codice è pensato per essere portabile, deve tenere conto del fatto che un ritorno NULL da malloc(0)non è un errore. Quindi, perché non assegnare artistcomunque NULL , dal momento che è un risultato di successo valido, è meno codice e non farà sì che i programmatori di manutenzione impieghino del tempo per capirlo?

malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)o malloc(some_variable_which_might_be_zero)forse potrebbero avere i loro usi, anche se ancora una volta devi fare molta attenzione a non trattare un ritorno NULL come un fallimento se il valore è 0, ma una dimensione 0 dovrebbe essere OK.


3

Ci sono molte risposte mezze vere qui intorno, quindi ecco i fatti concreti. La pagina di malloc()manuale per dice:

Se la dimensione è 0, malloc () restituisce NULL o un valore di puntatore univoco che può essere successivamente passato con successo a free ().

Ciò significa che non vi è assolutamente alcuna garanzia che il risultato di malloc(0)sia univoco o non NULL. L'unica garanzia è data dalla definizione di free(), ancora una volta, ecco cosa dice la pagina di manuale:

Se ptr è NULL, non viene eseguita alcuna operazione.

Quindi, qualunque cosa malloc(0)ritorni, può essere tranquillamente passata a free(). Ma anche un NULLpuntatore può farlo .

Di conseguenza, la scrittura non artist = malloc(0); è in alcun modo migliore della scrittura artist = NULL;


1
Peccato che l'implementazione non sia autorizzata a restituire un puntatore non nullo, non univoco. In questo modo, malloc(0)potrebbe restituire, diciamo, 0x1, e free()potrebbe avere un controllo in caso speciale di 0x1 proprio come ha fatto per 0x0.
Todd Lehman

3
@Todd Lehman Un'implementazione può funzionare come suggerisci. La specifica C non specifica che il risultato deve essere " NULLo un puntatore univoco". invece "o un puntatore nullo o un puntatore allo spazio allocato". Non vi è alcun requisito univoco . OTOH, la restituzione di un valore speciale non univoco può interrompere il codice che conta su valori univoci. Forse una domanda d'angolo per SO.
chux - Ripristina Monica il

manpuò anche documentare la forma definita dall'implementazione usata in * nix. In questo caso non è così, ma non è ancora una fonte canonica per il generale C.
Lundin

@ Lundin True. Ma le pagine di manuale sono molto più accessibili dello standard C, e le pagine di manuale sui sistemi GNU / Linux generalmente documentano abbastanza bene quale standard segue l'implementazione. Insieme alle informazioni su quali parti aderiscono a quale standard, se differiscono. Ho la sensazione che entrambi vogliano essere precisi e pubblicizzare ogni singolo bit che è un'estensione GNU ...
cmaster - reinstate monica

3

Perché non dovresti farlo ...

Poiché il valore restituito da malloc dipende dall'implementazione, potresti ricevere un puntatore NULL o un altro indirizzo. Questo può finire per creare overflow dell'heap-buffer se il codice di gestione degli errori non controlla sia la dimensione che il valore restituito, portando a problemi di stabilità (arresti anomali) o problemi di sicurezza anche peggiori.

Considera questo esempio, in cui un ulteriore accesso alla memoria tramite l'indirizzo restituito danneggerà l'heap se la dimensione è zero e l'implementazione restituisce un valore non NULL.

size_t size;

/* Initialize size, possibly by user-controlled input */

int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

Vedi questa pagina di codifica sicura dagli standard di codifica CERT dove ho preso l'esempio sopra per ulteriori letture.


Il collegamento è stato spostato: wiki.sei.cmu.edu/confluence/display/c/…
Cacahuete Frito

2

Certo, non l'ho mai visto prima, questa è la prima volta che vedo questa sintassi, si potrebbe dire, un classico caso di overkill di funzioni. Insieme alla risposta di Reed, vorrei sottolineare che esiste una cosa simile, che appare come una funzione sovraccarica realloc:

  • foo non è NULL e la dimensione è zero, realloc(foo, size); . Quando passi un puntatore non NULL e una dimensione pari a zero a realloc, realloc si comporta come se avessi chiamato free (...)
  • foo è NULL e la dimensione è diversa da zero e maggiore di 1 realloc(foo, size);,. Quando passi un puntatore NULL e la dimensione è diversa da zero, realloc si comporta come se avessi chiamato malloc (...)

Spero che questo aiuti, Cordiali saluti, Tom.


1

Per rispondere effettivamente alla domanda posta: non c'è motivo per farlo


0

malloc (0) restituirà NULL o un puntatore valido che può essere giustamente passato a free. E sebbene sembri che il ricordo a cui punta sia inutile o non possa essere scritto o letto, non è sempre vero. :)

int *i = malloc(0);
*i = 100;
printf("%d", *i);

Ci aspettiamo un errore di segmentazione qui, ma sorprendentemente, questo stampa 100! È perché malloc in realtà richiede un'enorme porzione di memoria quando chiamiamo malloc per la prima volta. Dopo ogni chiamata a malloc, utilizza la memoria di quel grosso pezzo. Solo dopo che quell'enorme pezzo è finito, viene richiesta nuova memoria.

Uso di malloc (0): se ti trovi in ​​una situazione in cui vuoi che le successive chiamate malloc siano più veloci, chiamare malloc (0) dovrebbe farlo per te (eccetto per i casi limite).


1
Scrivere su *ipotrebbe non andare in crash nel tuo caso, ma è comunque un comportamento indefinito. Attenzione ai demoni nasali!
John Dvorak

1
Sì. Questo è vero. È specifica dell'implementazione. L'ho verificato su MaxOS X e alcune distribuzioni Linux. Non l'ho provato su altre piattaforme. Detto questo, il concetto che ho descritto è stato descritto nel libro "Il linguaggio di programmazione C" di Brain Kernighan e Dennis Ritchie.
Sagar Bhosale

Lo so: commento in ritardo su questa domanda. Ma a volte c'è un uso per malloc(0)questo non menzionato. Su quelle implementazioni in cui restituisce un valore diverso da NULL, specialmente in una build DEBUG, probabilmente alloca PIÙ di quanto richiesto e ti dà il puntatore appena oltre la sua intestazione interna. Ciò consente di avere un'idea dell'effettivo utilizzo della memoria se lo si ottiene prima e dopo una serie di allocazioni. eg: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;o qualcosa di simile.
Jesse Chisholm

Ho letto "Il linguaggio di programmazione C" di Brain Kernighan e Dennis Ritchie e non ricordo che ne abbia parlato malloc(0). Potresti dire anche a quale capitolo ti riferisci? Sarebbe bello anche fornire una citazione esatta.
Андрей Беньковский

0

In Windows:

  • void *p = malloc(0);allocherà un buffer di lunghezza zero sull'heap locale. Il puntatore restituito è un puntatore di heap valido.
  • mallocalla fine chiama HeapAllocutilizzando l'heap di runtime C predefinito che quindi chiama RtlAllocateHeap, ecc.
  • free(p);utilizza HeapFreeper liberare il buffer di lunghezza 0 nell'heap. Non liberarlo comporterebbe una perdita di memoria.

0

In realtà è abbastanza utile e (ovviamente IMHO), il comportamento consentito di restituire un puntatore NULL è rotto. Un puntatore dinamico è utile non solo per ciò a cui punta, ma anche per il fatto che il suo indirizzo è univoco. La restituzione di NULL rimuove quella seconda proprietà. Tutti i mallocs incorporati che programma (abbastanza frequentemente in effetti) hanno questo comportamento.



-2

Ecco l'analisi dopo l'esecuzione con lo strumento di controllo della memoria valgrind.

==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740==     in use at exit: 0 bytes in 0 blocks
==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible

ed ecco il mio codice di esempio:

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

int main()
{
   //int i;
   char *p1;

   p1 = (char *)malloc(0);
   printf("p1 = %p\n", p1);

   free(p1);

   return 0;

}

Per impostazione predefinita, vengono allocati 1024 byte. Se aumento la dimensione di malloc, i byte allocati aumenteranno di 1025 e così via.


-3

Secondo la risposta di Reed Copsey e la pagina man di malloc, ho scritto alcuni esempi da testare. E ho scoperto che malloc (0) gli darà sempre un valore unico. Guarda il mio esempio:

char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

L'output sarà "Got a valid pointer", il che significa che ptrnon è nullo.


-6

malloc(0)restituirà un indirizzo di memoria valido e il cui intervallo dipenderà dal tipo di puntatore a cui viene allocata la memoria. Inoltre è possibile assegnare valori all'area di memoria, ma questo dovrebbe essere compreso nell'intervallo con il tipo di puntatore utilizzato. Puoi anche liberare la memoria allocata. Lo spiegherò con un esempio:

int *p=NULL;
p=(int *)malloc(0);
free(p);

Il codice sopra funzionerà bene in un gcccompilatore su macchina Linux. Se si dispone di un compilatore a 32 bit, è possibile fornire valori nell'intervallo intero, ad esempio da -2147483648 a 2147483647. Lo stesso vale anche per i caratteri. Si noti che se il tipo di puntatore dichiarato viene modificato, l'intervallo di valori cambierà indipendentemente dal malloctypecast, ad es

unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);

p prenderà un valore da 0 a 255 di char poiché è dichiarato un int senza segno.


5
Krellan ha ragione nel sottolineare che questa risposta è sbagliata: malloc()non sa nulla del cast (che in realtà è del tutto superfluente in C). Dereferenziare il valore restituito di malloc(0)richiamerà un comportamento non definito.
cmaster - ripristina monica il

-6

Solo per correggere una falsa impressione qui:

artist = (char *) malloc(0);non tornerà mai più NULL; non è lo stesso di artist = NULL;. Scrivi un semplice programma e confrontalo artistcon NULL. if (artist == NULL)è falso ed if (artist)è vero.

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.