Lancio il risultato di malloc?
In questa domanda , qualcuno ha suggerito in un commento che avrei dovuto non lanciare il risultato di malloc
, vale a dire
int *sieve = malloc(sizeof(int) * length);
piuttosto che:
int *sieve = (int *) malloc(sizeof(int) * length);
Perché dovrebbe essere così?
Risposte
No ; tu non lanci il risultato, dal momento che:
- Non è necessario, poiché
void *
in questo caso viene promosso automaticamente e in modo sicuro a qualsiasi altro tipo di puntatore. - Aggiunge confusione al codice, i cast non sono molto facili da leggere (specialmente se il tipo di puntatore è lungo).
- Ti fa ripetere te stesso, il che generalmente è un male.
- Può nascondere un errore se ti sei dimenticato di includere
<stdlib.h>
. Ciò può causare arresti anomali (o, peggio, non causare arresti anomali fino a molto tempo dopo in una parte completamente diversa del codice). Considera cosa succede se i puntatori e gli interi hanno dimensioni diverse; quindi stai nascondendo un avviso trasmettendo e potresti perdere parti del tuo indirizzo restituito. Nota: a partire da C99 le funzioni implicite sono passate da C, e questo punto non è più rilevante poiché non esiste l'ipotesi automatica che le funzioni non dichiarate ritorninoint
.
Per chiarire, nota che ho detto "non cast", non "non hai bisogno di cast". Secondo me, è un fallimento includere il cast, anche se hai capito bene. Semplicemente non ci sono vantaggi nel farlo, ma un sacco di potenziali rischi e l'inclusione del cast indica che non sei a conoscenza dei rischi.
Si noti inoltre, come sottolineano i commentatori, che quanto sopra parla di C semplice, non C ++. Credo fermamente in C e C ++ come linguaggi separati.
Per aggiungere altro, il codice ripete inutilmente le informazioni sul tipo ( int
) che possono causare errori. È meglio de-referenziare il puntatore utilizzato per memorizzare il valore restituito, per "bloccare" i due insieme:
int *sieve = malloc(length * sizeof *sieve);
Questo sposta anche length
in avanti per una maggiore visibilità e abbassa le parentesi ridondanti con sizeof
; essi sono necessari solo quando l'argomento è un nome di tipo. Molte persone sembrano non saperlo (o ignorare) questo, il che rende il loro codice più dettagliato. Ricorda: sizeof
non è una funzione! :)
Mentre spostarsi length
in primo piano può aumentare la visibilità in alcuni rari casi, si dovrebbe anche prestare attenzione che nel caso generale, dovrebbe essere meglio scrivere l'espressione come:
int *sieve = malloc(sizeof *sieve * length);
Poiché mantenere il sizeof
primo, in questo caso, garantisce che la moltiplicazione venga eseguita almeno con la size_t
matematica.
Confronta: malloc(sizeof *sieve * length * width)
vs. malloc(length * width * sizeof *sieve)
il secondo può traboccare il length * width
quando width
e length
sono tipi più piccoli di size_t
.
In C, non è necessario eseguire il cast del valore restituito di malloc
. Il puntatore a void restituito da malloc
viene convertito automaticamente nel tipo corretto. Tuttavia, se vuoi che il tuo codice si compili con un compilatore C ++, è necessario un cast. Un'alternativa preferita dalla comunità è usare quanto segue:
int *sieve = malloc(sizeof *sieve * length);
che inoltre ti libera dal doverti preoccupare di cambiare il lato destro dell'espressione se mai cambi il tipo di sieve
.
I calchi sono pessimi, come hanno sottolineato le persone. Soprattutto i cast di puntatori.
Si fa lanci, in quanto:
- Rende il tuo codice più portabile tra C e C ++ e, come dimostra l'esperienza di SO, molti programmatori affermano di scrivere in C quando stanno davvero scrivendo in C ++ (o C più estensioni del compilatore locale).
- In caso contrario, si può nascondere un errore : nota tutti gli esempi SO di confusione quando scrivere
type *
rispetto atype **
. - L'idea che ti impedisce di notare che non sei riuscito a
#include
un file di intestazione appropriato manca la foresta per gli alberi . È come dire "non preoccuparti per il fatto che non hai chiesto al compilatore di lamentarsi di non vedere i prototipi - quel fastidioso stdlib.h è la cosa VERAMENTE importante da ricordare!" - Forza un controllo incrociato cognitivo extra . Mette il (presunto) tipo desiderato proprio accanto all'aritmetica che stai facendo per la dimensione grezza di quella variabile. Scommetto che potresti fare uno studio SO che mostra che i
malloc()
bug vengono catturati molto più velocemente quando c'è un cast. Come per le asserzioni, le annotazioni che rivelano l'intento riducono i bug. - Ripetersi in un modo che la macchina possa controllare è spesso un'ottima idea. In effetti, questo è ciò che è un'affermazione, e questo uso del cast è un'affermazione. Le asserzioni sono ancora la tecnica più generale che abbiamo per ottenere il codice corretto, da quando Turing ha avuto l'idea tanti anni fa.
Come altri hanno affermato, non è necessario per C, ma necessario per C ++. Se pensi di compilare il tuo codice C con un compilatore C ++, per qualsiasi motivo, puoi invece usare una macro, come:
#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif
In questo modo puoi ancora scriverlo in un modo molto compatto:
int *sieve = NEW(int, 1);
e verrà compilato per C e C ++.
Da Wikipedia :
Vantaggi del casting
L'inclusione del cast può consentire a un programma o una funzione C di compilarsi come C ++.
Il cast consente versioni precedenti al 1989 di malloc che originariamente restituivano un carattere *.
Il casting può aiutare lo sviluppatore a identificare le incongruenze nel dimensionamento del tipo se il tipo di puntatore di destinazione cambia, in particolare se il puntatore è dichiarato lontano dalla chiamata malloc () (sebbene i compilatori moderni e gli analizzatori statici possano avvisare su tale comportamento senza richiedere il cast).
Svantaggi del casting
Secondo lo standard ANSI C, il cast è ridondante.
L'aggiunta del cast può mascherare la mancata inclusione dell'intestazione stdlib.h , in cui si trova il prototipo per malloc. In assenza di un prototipo per malloc, lo standard richiede che il compilatore C presupponga che malloc restituisca un int. Se non è presente alcun cast, viene emesso un avviso quando questo numero intero viene assegnato al puntatore; tuttavia, con il cast, questo avviso non viene prodotto, nascondendo un bug. Su alcune architetture e modelli di dati (come LP64 su sistemi a 64 bit, dove long e puntatori sono a 64 bit e int a 32 bit), questo errore può effettivamente comportare un comportamento indefinito, poiché il malloc dichiarato implicitamente restituisce un 32- valore di bit mentre la funzione effettivamente definita restituisce un valore a 64 bit. A seconda delle convenzioni di chiamata e del layout della memoria, ciò potrebbe causare lo stack smashing. È meno probabile che questo problema passi inosservato nei compilatori moderni, poiché producono in modo uniforme avvisi che indicano che è stata utilizzata una funzione non dichiarata, quindi verrà comunque visualizzato un avviso. Ad esempio, il comportamento predefinito di GCC è mostrare un avviso che recita "dichiarazione implicita incompatibile della funzione incorporata" indipendentemente dal fatto che il cast sia presente o meno.
Se il tipo del puntatore viene modificato nella sua dichiarazione, potrebbe essere necessario modificare tutte le righe in cui malloc viene chiamato e lanciato.
Sebbene malloc senza casting sia il metodo preferito ei programmatori più esperti lo scelgono , dovresti usare quello che preferisci essere a conoscenza dei problemi.
cioè: se hai bisogno di compilare un programma C come C ++ (sebbene sia un linguaggio separato) devi eseguire il cast del risultato dell'uso malloc
.
In C puoi convertire implicitamente un void
puntatore in qualsiasi altro tipo di puntatore, quindi non è necessario un cast. Usarne uno può suggerire all'osservatore occasionale che c'è qualche motivo per cui è necessario, il che può essere fuorviante.
Non si esegue il cast del risultato di malloc
, perché così facendo si aggiunge un inutile disordine al codice.
Il motivo più comune per cui le persone lanciano il risultato di malloc
è perché non sono sicuri di come funziona il linguaggio C. Questo è un segnale di avvertimento: se non sai come funziona un particolare meccanismo linguistico, non indovinare. Cercalo o chiedi su Stack Overflow.
Alcuni commenti:
Un puntatore void può essere convertito in / da qualsiasi altro tipo di puntatore senza un cast esplicito (C11 6.3.2.3 e 6.5.16.1).
C ++ tuttavia non consentirà un cast implicito tra
void*
e un altro tipo di puntatore. Quindi in C ++, il cast sarebbe stato corretto. Ma se programmi in C ++, dovresti usarenew
e nonmalloc()
. E non dovresti mai compilare codice C usando un compilatore C ++.Se è necessario supportare sia C che C ++ con lo stesso codice sorgente, utilizzare le opzioni del compilatore per contrassegnare le differenze. Non tentare di soddisfare entrambi gli standard linguistici con lo stesso codice, perché non sono compatibili.
Se un compilatore C non riesce a trovare una funzione perché hai dimenticato di includere l'intestazione, riceverai un errore del compilatore / linker al riguardo. Quindi se ti sei dimenticato di includere
<stdlib.h>
questo non è un problema, non sarai in grado di creare il tuo programma.Sugli antichi compilatori che seguono una versione dello standard che ha più di 25 anni, dimenticare di includere
<stdlib.h>
comporterebbe un comportamento pericoloso. Perché in quell'antico standard, le funzioni senza un prototipo visibile convertivano implicitamente il tipo restituito inint
. Il cast del risultato damalloc
esplicitamente nasconderebbe quindi questo bug.Ma questo è davvero un non problema. Non stai usando un computer vecchio di 25 anni, quindi perché dovresti usare un compilatore di 25 anni?
In C si ottiene una conversione implicita da void *
a qualsiasi altro puntatore (dati).
Il cast del valore restituito da malloc()
non è necessario ora, ma vorrei aggiungere un punto che sembra nessuno abbia sottolineato:
Nei tempi antichi, cioè prima che ANSI C fornisse il void *
tipo generico di puntatori, era char *
il tipo per tale utilizzo. In tal caso, il cast può chiudere gli avvisi del compilatore.
Riferimento: C FAQ
Solo aggiungendo la mia esperienza, studiando ingegneria informatica vedo che i due o tre professori che ho visto scrivere in C hanno sempre lanciato malloc, tuttavia quello che ho chiesto (con un immenso CV e comprensione di C) mi ha detto che è assolutamente inutile ma solo per essere assolutamente specifico e per portare gli studenti nella mentalità di essere assolutamente specifici. Essenzialmente il casting non cambierà nulla nel modo in cui funziona, fa esattamente quello che dice, alloca la memoria e il casting non ha effetto, ottieni la stessa memoria e anche se lo lanci su qualcos'altro per errore (e in qualche modo eviti il compilatore errori) C accederà allo stesso modo.
Modifica: il casting ha un certo punto. Quando si utilizza la notazione array, il codice generato deve sapere quanti posti di memoria deve avanzare per raggiungere l'inizio dell'elemento successivo, questo si ottiene tramite il casting. In questo modo sai che per un doppio vai avanti di 8 byte mentre per un int vai di 4 e così via. Quindi non ha effetto se usi la notazione puntatore, nella notazione array diventa necessario.
Non è obbligatorio eseguire il cast dei risultati di malloc
, poiché restituisce void*
e a void*
può essere puntato a qualsiasi tipo di dati.
Questo è ciò che dice il manuale di riferimento della libreria GNU C :
È possibile memorizzare il risultato di
malloc
in qualsiasi variabile del puntatore senza cast, poiché ISO C converte automaticamente il tipovoid *
in un altro tipo di puntatore quando necessario. Ma il cast è necessario in contesti diversi dagli operatori di assegnazione o se si desidera che il codice venga eseguito in C.
E in effetti lo standard ISO C11 (p347) lo dice:
Il puntatore restituito se l'allocazione ha successo è opportunamente allineato in modo che possa essere assegnato a un puntatore a qualsiasi tipo di oggetto con un requisito di allineamento fondamentale e quindi utilizzato per accedere a tale oggetto oa un array di tali oggetti nello spazio allocato (fino al lo spazio è esplicitamente deallocato)
Un puntatore void è un puntatore a un oggetto generico e C supporta la conversione implicita da un tipo di puntatore void ad altri tipi, quindi non è necessario tipizzarlo esplicitamente.
Tuttavia, se vuoi che lo stesso codice funzioni perfettamente compatibile su una piattaforma C ++, che non supporta la conversione implicita, devi fare il typecasting, quindi tutto dipende dall'usabilità.
Il tipo restituito è void *, di cui è possibile eseguire il cast al tipo di puntatore ai dati desiderato per essere dereferenziato.
Dipende dal linguaggio di programmazione e dal compilatore. Se usi malloc
in C, non è necessario digitare cast, poiché digiterà automaticamente cast. Tuttavia, se stai usando C ++, dovresti digitare cast perché malloc
restituirà un void*
tipo.
Nel linguaggio C, un puntatore void può essere assegnato a qualsiasi puntatore, motivo per cui non dovresti usare un cast di tipo. Se vuoi un'allocazione "type safe", posso consigliare le seguenti funzioni macro, che utilizzo sempre nei miei progetti C:
#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)
Con questi a posto puoi semplicemente dire
NEW_ARRAY(sieve, length);
Per gli array non dinamici, la terza macro di funzione indispensabile è
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
che rende i loop di array più sicuri e più convenienti:
int i, a[100];
for (i = 0; i < LEN(a); i++) {
...
}
Le persone abituate a GCC e Clang sono viziate. Non è poi così bello là fuori.
Sono stato piuttosto inorridito nel corso degli anni dai compilatori incredibilmente vecchi che mi è stato richiesto di usare. Spesso le aziende ei manager adottano un approccio ultra-conservatore per cambiare i compilatori e non testeranno nemmeno se un nuovo compilatore (con una migliore conformità agli standard e ottimizzazione del codice) funzionerà nel loro sistema. La realtà pratica per gli sviluppatori che lavorano è che quando si codifica è necessario coprire le proprie basi e, sfortunatamente, eseguire il casting di mallocs è una buona abitudine se non è possibile controllare quale compilatore può essere applicato al codice.
Suggerirei anche che molte organizzazioni applichino uno standard di codifica proprio e che questo dovrebbe essere il metodo seguito dalle persone se definito. In assenza di una guida esplicita, tendo a scegliere molto probabilmente la compilazione ovunque, piuttosto che l'adesione servile a uno standard.
L'argomento che non è necessario secondo gli standard attuali è abbastanza valido. Ma questo argomento omette gli aspetti pratici del mondo reale. Non codifichiamo in un mondo governato esclusivamente dallo standard del giorno, ma dagli aspetti pratici di quello che mi piace chiamare "campo della realtà del management locale". E questo è piegato e contorto più di quanto lo sia mai stato lo spazio tempo. :-)
YMMV.
Tendo a pensare a lanciare malloc come un'operazione difensiva. Non carino, non perfetto, ma generalmente sicuro. (Onestamente, se non hai incluso stdlib.h allora hai modo più problemi di fusione malloc!).
No, non lanci il risultato di malloc()
.
In generale, non trasmetti ao davoid *
.
Un motivo tipico addotto per non farlo è che il mancato rispetto di questa procedura #include <stdlib.h>
potrebbe passare inosservato. Questo non è più un problema da molto tempo poiché C99 ha reso illegali le dichiarazioni di funzioni implicite , quindi se il tuo compilatore è conforme almeno a C99, riceverai un messaggio di diagnostica.
Ma c'è una ragione molto più forte per non introdurre cast di puntatori non necessari:
In C, il cast di un puntatore è quasi sempre un errore . Ciò è dovuto alla seguente regola ( §6.5 p7 in N1570, l'ultima bozza per C11):
Un oggetto deve avere il suo valore memorizzato accessibile solo da un'espressione lvalue che ha uno dei seguenti tipi:
- un tipo compatibile con il tipo effettivo dell'oggetto,
- una versione qualificata di un tipo compatibile con il tipo effettivo dell'oggetto,
- un tipo che è il tipo firmato o non firmato corrispondente al tipo effettivo dell'oggetto,
- un tipo che è il tipo firmato o non firmato corrispondente a una versione qualificata del tipo effettivo dell'oggetto,
- un tipo aggregato o unione che ne include uno dei suddetti tipi tra i suoi membri (incluso, ricorsivamente, un membro di un'unione subaggregata o contenuta), o
- un tipo di carattere.
Questa è anche nota come regola di aliasing rigoroso . Quindi il codice seguente è un comportamento non definito :
long x = 5;
double *p = (double *)&x;
double y = *p;
E, a volte sorprendentemente, anche il seguente è:
struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;
A volte, si fa necessario puntatori del cast, ma data la rigida regola di aliasing , bisogna stare molto attenti con esso. Quindi, qualsiasi occorrenza di un puntatore cast nel codice è un punto in cui devi ricontrollare la sua validità . Pertanto, non si scrive mai un cast di puntatore non necessario.
tl; dr
In poche parole: poiché in C, qualsiasi occorrenza di un cast di puntatore dovrebbe sollevare una bandiera rossa per il codice che richiede un'attenzione speciale, non dovresti mai scrivere cast di puntatori non necessari .
Note a margine:
Ci sono casi in cui hai effettivamente bisogno di un cast
void *
, ad esempio se vuoi stampare un puntatore:int x = 5; printf("%p\n", (void *)&x);
Il cast è necessario qui, perché
printf()
è una funzione variadica, quindi le conversioni implicite non funzionano.In C ++, la situazione è diversa. Il cast di tipi di puntatore è piuttosto comune (e corretto) quando si ha a che fare con oggetti di classi derivate. Pertanto, è logico che in C ++, la conversione da e
void *
è non implicito. C ++ ha un intero set di diversi gusti di casting.
Ho inserito il cast semplicemente per mostrare disapprovazione per il brutto buco nel sistema di tipi, che consente la compilazione di codice come il seguente frammento senza diagnostica, anche se non vengono utilizzati cast per provocare la cattiva conversione:
double d;
void *p = &d;
int *q = p;
Vorrei che non esistesse (e non è in C ++) e quindi ho lanciato. Rappresenta il mio gusto e la mia politica di programmazione. Non sto solo lanciando un suggerimento, ma in modo efficace, lanciando un voto e scacciando i demoni della stupidità . Se non posso davvero scacciare la stupidità , lasciami almeno esprimere il desiderio di farlo con un gesto di protesta.
In effetti, una buona pratica è quella di racchiudere malloc
(e amici) con funzioni che restituiscono unsigned char *
e fondamentalmente non usarle mai void *
nel codice. Se hai bisogno di un puntatore generico a qualsiasi oggetto, usa un char *
or unsigned char *
e hai cast in entrambe le direzioni. L'unico relax a cui si può concedere, forse, è usare funzioni come memset
e memcpy
senza calchi.
Sull'argomento del casting e della compatibilità C ++, se scrivi il tuo codice in modo che venga compilato sia come C che C ++ (nel qual caso devi eseguire il cast del valore di ritorno malloc
quando lo assegni a qualcosa di diverso da void *
), puoi fare un molto utile cosa per te: puoi usare macro per il casting che si traducono in cast in stile C ++ durante la compilazione come C ++, ma riducono a un cast in C quando compili come C:
/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif
Se aderisci a queste macro, una semplice grep
ricerca della tua base di codice per questi identificatori ti mostrerà dove sono tutti i tuoi cast, in modo da poter verificare se qualcuno di essi è errato.
Quindi, andando avanti, se compili regolarmente il codice con C ++, imporrà l'uso di un cast appropriato. Ad esempio, se usi strip_qual
solo per rimuovere un const
o volatile
, ma il programma cambia in modo tale che ora sia coinvolta una conversione di tipo, otterrai una diagnostica e dovrai usare una combinazione di cast per ottenere la conversione desiderata.
Per aiutarti ad aderire a queste macro, il compilatore GNU C ++ (non C!) Ha una bella caratteristica: una diagnostica opzionale che viene prodotta per tutte le occorrenze di cast in stile C.
-Wold-style-cast (solo C ++ e Objective-C ++) Avvisa se viene utilizzato un cast vecchio stile (stile C) su un tipo non vuoto all'interno di un programma C ++. I cast di nuovo stile (dynamic_cast, static_cast, reinterpret_cast e const_cast) sono meno vulnerabili ad effetti indesiderati e molto più facili da cercare.
Se il codice C viene compilato come C ++, è possibile utilizzare questa -Wold-style-cast
opzione per scoprire tutte le occorrenze della (type)
sintassi di casting che potrebbe insinuarsi nel codice e seguire queste diagnostiche sostituendole con una scelta appropriata tra le macro precedenti (o un combinazione, se necessario).
Questo trattamento delle conversioni è la più grande giustificazione tecnica standalone per lavorare in un "C pulito": il dialetto combinato C e C ++, che a sua volta giustifica tecnicamente il casting del valore di ritorno di malloc
.
Preferisco fare il cast, ma non manualmente. Il mio preferito è usare g_new
e le g_new0
macro di glib. Se glib non viene utilizzato, aggiungerei macro simili. Queste macro riducono la duplicazione del codice senza compromettere la sicurezza dei tipi. Se si sbaglia il tipo, si otterrebbe un cast implicito tra i puntatori non void, che causerebbe un avviso (errore in C ++). Se dimentichi di includere l'intestazione che definisce g_new
e g_new0
, riceverai un errore. g_new
ed g_new0
entrambi accettano gli stessi argomenti, a differenza di malloc
questo richiede meno argomenti di calloc
. Basta aggiungere 0
per ottenere memoria inizializzata a zero. Il codice può essere compilato con un compilatore C ++ senza modifiche.
La cosa migliore da fare quando si programma in C ogni volta che è possibile:
- Fai compilare il tuo programma tramite un compilatore C con tutti gli avvisi attivati
-Wall
e correggi tutti gli errori e gli avvisi - Assicurati che non ci siano variabili dichiarate come
auto
- Quindi compilarlo usando un compilatore C ++ con
-Wall
e-std=c++11
. Correggi tutti gli errori e gli avvisi. - Ora compila di nuovo usando il compilatore C. Il tuo programma dovrebbe ora compilarsi senza alcun avviso e contenere meno bug.
Questa procedura consente di sfruttare il controllo del tipo rigoroso C ++, riducendo così il numero di bug. In particolare, questa procedura ti obbliga a includere stdlib.h
o otterrai
malloc
non è stato dichiarato in questo ambito
e ti costringe anche a lanciare il risultato di malloc
o otterrai
conversione non valida da
void*
aT*
o qualunque sia il tuo tipo di target.
Gli unici vantaggi derivanti dalla scrittura in C anziché in C ++ che posso trovare sono
- C ha un ABI ben specificato
- C ++ può generare più codice [eccezioni, RTTI, modelli, polimorfismo di runtime ]
Si noti che il secondo svantaggio dovrebbe nel caso ideale scomparire quando si utilizza il sottoinsieme comune a C insieme alla caratteristica polimorfica statica .
Per coloro che trovano scomode regole rigide di C ++, possiamo usare la funzionalità C ++ 11 con tipo dedotto
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
Il casting è solo per C ++ non C. Nel caso in cui si utilizzi un compilatore C ++ è meglio cambiarlo in compilatore C.
Il casting di malloc non è necessario in C ma obbligatorio in C ++.
Il casting non è necessario in C a causa di:
void *
viene promosso automaticamente e in modo sicuro a qualsiasi altro tipo di puntatore nel caso di C.- Può nascondere un errore se ti sei dimenticato di includere
<stdlib.h>
. Ciò può causare arresti anomali. - Se i puntatori e gli interi sono di dimensioni diverse, stai nascondendo un avviso eseguendo il casting e potresti perdere bit dell'indirizzo restituito.
- Se il tipo del puntatore viene modificato al momento della sua dichiarazione, potrebbe essere necessario modificare anche tutte le righe in cui
malloc
viene chiamato e lanciato.
D'altra parte, il casting può aumentare la portabilità del tuo programma. cioè, consente a un programma o una funzione C di compilarsi come C ++.
Il concetto alla base del puntatore void è che può essere eseguito il cast su qualsiasi tipo di dati, ecco perché malloc restituisce void. Inoltre devi essere consapevole del typecasting automatico. Quindi non è obbligatorio lanciare il puntatore anche se devi farlo. Aiuta a mantenere il codice pulito e aiuta il debug
Un puntatore void è un puntatore generico e C supporta la conversione implicita da un tipo di puntatore void ad altri tipi, quindi non è necessario tipizzarlo esplicitamente.
Tuttavia, se vuoi che lo stesso codice funzioni perfettamente compatibile su una piattaforma C ++, che non supporta la conversione implicita, devi fare il typecasting, quindi tutto dipende dall'usabilità.
Come altri hanno affermato, non è necessario per C, ma per C ++.
L'inclusione del cast può consentire a un programma o una funzione C di compilarsi come C ++.
In C non è necessario, poiché void * viene promosso automaticamente e in modo sicuro a qualsiasi altro tipo di puntatore.
Ma se esegui il cast, può nascondere un errore se hai dimenticato di includere stdlib.h . Ciò può causare arresti anomali (o, peggio, non causare arresti anomali fino a molto tempo dopo in una parte completamente diversa del codice).
Poiché stdlib.h contiene il prototipo di malloc è stato trovato. In assenza di un prototipo per malloc, lo standard richiede che il compilatore C presupponga che malloc restituisca un int. Se non è presente alcun cast, viene emesso un avviso quando questo numero intero viene assegnato al puntatore; tuttavia, con il cast, questo avviso non viene prodotto, nascondendo un bug.
Questa domanda è oggetto di abuso basato sull'opinione.
A volte noto commenti del genere:
Non lanciare il risultato di malloc
o
Perché non lanci il risultato di malloc
sulle domande in cui OP utilizza il casting. I commenti stessi contengono un collegamento ipertestuale a questa domanda.
Questo è in ogni modo possibile inappropriato e scorretto. Non c'è giusto né sbagliato quando si tratta veramente del proprio stile di programmazione.
Perché sta succedendo?
Si basa su due motivi:
Questa domanda è davvero basata sull'opinione. Tecnicamente, la questione avrebbe dovuto essere chiusa in quanto basata sull'opinione pubblica anni fa. Una domanda " Do I " o " Don't I " o equivalente " Dovrei " o " Non dovrei ", semplicemente non puoi rispondere in modo mirato senza un atteggiamento della propria opinione. Uno dei motivi per chiudere una domanda è perché "potrebbe portare a risposte basate sull'opinione", come è ben mostrato qui.
Molte risposte (inclusa la risposta più evidente e accettata di @unwind ) sono completamente o quasi interamente basate sull'opinione (ad esempio un misterioso "disordine" che verrebbe aggiunto al tuo codice se esegui il casting o se ti ripeti sarebbe male) e mostrano una chiara e mirata tendenza a omettere il cast. Discutono sulla ridondanza del cast da un lato ma anche e peggio sostengono di risolvere un bug causato da un bug / fallimento della programmazione stessa - a no
#include <stdlib.h>
se si vuole usaremalloc()
.
Voglio portare una visione fedele di alcuni punti discussi, con meno della mia opinione personale. In particolare, è necessario sottolineare alcuni punti:
Una domanda così suscettibile per cadere nella propria opinione necessita di una risposta con pro e contro neutrali. Non solo contro o pro.
Una buona panoramica dei pro e dei contro è elencata in questa risposta:
https://stackoverflow.com/a/33047365/12139179
(Personalmente lo considero per questo motivo la migliore risposta, finora.)
Una ragione che si incontra al massimo per giustificare l'omissione del cast è che il cast potrebbe nascondere un bug.
Se qualcuno usa un implicito dichiarato
malloc()
che ritornaint
(le funzioni implicite sono andate dallo standard da C99) esizeof(int) != sizeof(int*)
, come mostrato in questa domandaPerché questo codice segfault su architettura a 64 bit ma funziona bene su 32 bit?
il cast avrebbe nascosto un bug.
Anche se questo è vero, mostra solo metà della storia poiché l'omissione del cast sarebbe solo una soluzione avanzata a un bug ancora più grande, escluso
stdlib.h
quando si usamalloc()
.Questo non sarà mai un problema serio, se tu,
Utilizzare un compilatore conforme a C99 o superiore (che è consigliato e dovrebbe essere obbligatorio) e
Non sono così assenti da dimenticarsi di includere
stdlib.h
, quando si desidera utilizzaremalloc()
nel proprio codice, che è di per sé un enorme bug.
Alcune persone discutono sulla conformità C ++ del codice C, poiché il cast è obbligatorio in C ++.
Prima di tutto da dire in generale: compilare codice C con un compilatore C ++ non è una buona pratica.
C e C ++ sono infatti due linguaggi completamente diversi con semantica diversa.
Ma se vuoi / hai davvero bisogno di rendere il codice C compatibile con C ++ e viceversa usa le opzioni del compilatore invece di qualsiasi cast.
Poiché il cast è con tendenza dichiarato ridondante o addirittura dannoso, voglio concentrarmi su queste domande, che danno buone ragioni per cui il casting può essere utile o addirittura necessario:
https://stackoverflow.com/a/34094068/12139179
https://stackoverflow.com/a/36297486/12139179
https://stackoverflow.com/a/33044300/12139179
- Il cast può non essere vantaggioso quando il tuo codice, rispettivamente il tipo di puntatore assegnato (e con questo il tipo di cast), cambia, sebbene ciò sia nella maggior parte dei casi improbabile. Quindi dovresti mantenere / modificare anche tutti i cast e se hai qualche migliaio di chiamate alle funzioni di gestione della memoria nel tuo codice, questo può davvero riassumere e diminuire l'efficienza della manutenzione.
Sommario:
Il fatto è che il cast è ridondante per lo standard C (già da ANSI-C (C89 / C90)) se il puntatore assegnato punta a un oggetto di requisito fondamentale di allineamento (che include la maggior parte di tutti gli oggetti).
Non è necessario eseguire il cast poiché il puntatore viene allineato automaticamente in questo caso:
"L'ordine e la contiguità della memoria allocata dalle chiamate successive alle funzioni align_alloc, calloc, malloc e realloc non sono specificati. Il puntatore restituito se l'allocazione riesce è adeguatamente allineato in modo che possa essere assegnato a un puntatore a qualsiasi tipo di oggetto con un requisito fondamentale di allineamento e quindi utilizzato per accedere a tale oggetto oa un array di tali oggetti nello spazio allocato (fino a quando lo spazio non viene esplicitamente deallocato). "
Fonte: C18, §7.22.3 / 1
"Un allineamento fondamentale è un allineamento valido inferiore o uguale a
_Alignof (max_align_t)
. Gli allineamenti fondamentali devono essere supportati dall'implementazione per oggetti di tutte le durate di conservazione. I requisiti di allineamento dei seguenti tipi devono essere allineamenti fondamentali:- tutti i tipi di base atomici, qualificati o non qualificati;
- tutti i tipi enumerati atomici, qualificati o non qualificati;
- tutti i tipi di puntatore atomico, qualificato o non qualificato;
- tutti i tipi di array il cui tipo di elemento ha un requisito di allineamento fondamentale; 57)
- tutti i tipi specificati nella clausola 7 come tipi di oggetti completi;
- tutti i tipi di struttura o unione i cui elementi hanno tipi con requisiti di allineamento fondamentali e nessuno dei cui elementi ha un identificatore di allineamento che specifica un allineamento che non è un allineamento fondamentale.
- Come specificato in 6.2.1, la dichiarazione successiva potrebbe nascondere la dichiarazione precedente. "
Fonte: C18, §6.2.8 / 2
Tuttavia, se si alloca memoria per un oggetto definito dall'implementazione con requisiti di allineamento esteso, il cast sarà necessario.
Un allineamento esteso è rappresentato da un allineamento maggiore di
_Alignof (max_align_t)
. È definito dall'implementazione se gli allineamenti estesi sono supportati e le durate di archiviazione per cui sono supportati. Un tipo con requisiti di allineamento esteso è un tipo sovra-allineato.58)Fonte. C18, §6.2.8 / 3
Tutto il resto dipende dal caso d'uso specifico e dalla propria opinione.
Fai attenzione a come ti istruisci.
Ti consiglio di leggere prima con attenzione tutte le risposte fatte finora (così come i loro commenti che potrebbero indicare un fallimento) e poi di costruire la tua opinione se tu o se non lanci il risultato di malloc()
un caso specifico.
Notare che:
Non esiste una risposta giusta o sbagliata a questa domanda. È una questione di stile e tu stesso decidi quale strada scegliere (se non sei costretto dall'istruzione o dal lavoro ovviamente). Si prega di essere consapevoli di questo e non lasciarti ingannare .
Last note: I voted to lately close this question as opinion-based, which is indeed needed since years. If you got the close/reopen privilege I would like to invite you to do so, too.
For me, the take home and conclusion here is that casting malloc
in C is totally NOT necessary but if you however cast, it wont affect malloc
as malloc
will still allocate to you your requested blessed memory space. Another take home is the reason or one of the reasons people do casting and this is to enable them compile same program either in C or C++.
There may be other reasons but other reasons, almost certainly, would land you in serious trouble sooner or later.
You can, but don't need to cast in C. You have to cast if that code is compiled as C++.