Devo lanciare il risultato di malloc?


2408

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ì?


222
Inoltre, è più gestibile scrivere sieve = malloc (sizeof * sieve * length);
William Pursell,


3
Le risposte qui sono un campo minato di unilaterale. La risposta è, dipende". Non è una cosa necessaria per far funzionare il programma. Tuttavia, alcuni standard di codifica lo richiedono ... Ad esempio, vedi Standard di codifica CERT C
Dr. Person Person II

Stranamente, tutti concordano sul fatto che non si lancia NULL. (Questo è probabilmente il motivo per cui C ++ ha introdotto nullptr: C ++ non consente alcun cast di puntatori impliciti)
Sapphire_Brick

Puoi, ma non è necessario. Ma è necessario in C ++.
user12211554

Risposte:


2217

No ; tu non lanci il risultato, dal momento che:

  • Non è necessario, poiché void *viene promosso automaticamente e in modo sicuro a qualsiasi altro tipo di puntatore in questo caso.
  • Aggiunge disordine 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 negativo.
  • Può nascondere un errore se hai dimenticato di includere <stdlib.h>. Ciò può causare arresti anomali (o, peggio, non causare un arresto fino a molto tempo dopo in una parte completamente diversa del codice). Considera cosa succede se puntatori e numeri interi hanno dimensioni diverse; allora stai nascondendo un avviso lanciando e potresti perdere bit del tuo indirizzo di ritorno. Nota: a partire da C99 le funzioni implicite sono passate da C, e questo punto non è più rilevante poiché non si presume automaticamente che le funzioni non dichiarate ritornino int.

Come un chiarimento, si noti che ho detto "tu non gettato", non "non c'è bisogno di cast". Secondo me, non è possibile includere il cast, anche se hai capito bene. Semplicemente non ci sono benefici nel farlo, ma un sacco di potenziali rischi, e incluso il cast indica che non si conoscono i rischi.

Nota anche, come sottolineano i commentatori, che quanto sopra parla della C diritta, non del C ++. Credo fermamente in C e C ++ come lingue separate.

Per aggiungere ulteriormente, il codice ripete inutilmente le informazioni sul tipo ( int) che possono causare errori. È meglio rimuovere il riferimento al puntatore utilizzato per memorizzare il valore restituito, per "bloccare" i due insieme:

int *sieve = malloc(length * sizeof *sieve);

Questo sposta anche lengthin avanti per una maggiore visibilità e fa cadere le parentesi ridondanti con sizeof; essi sono necessari solo quando l'argomento è un nome di tipo. Molte persone sembrano non saperlo (o ignorarlo), il che rende il loro codice più dettagliato. Ricorda: sizeofnon è una funzione! :)


Mentre spostarsi lengthin avanti può aumentare la visibilità in alcuni rari casi, si dovrebbe anche prestare attenzione al fatto che, nel caso generale, dovrebbe essere meglio scrivere l'espressione come:

int *sieve = malloc(sizeof *sieve * length);

Dal momento che mantenere il sizeofprimo, in questo caso, si assicura che la moltiplicazione sia fatta almeno con la size_tmatematica.

Confronta: malloc(sizeof *sieve * length * width)rispetto malloc(length * width * sizeof *sieve)al secondo potrebbe traboccare il length * widthquando widthe lengthsono tipi più piccoli di size_t.


21
Si prega di considerare l'aggiornamento della risposta. Il cast non è più pericoloso e ripetersi non è necessariamente una cosa negativa (la ridondanza può aiutare a rilevare gli errori).
n. 'pronomi' m.

11
I compilatori sono cambiati. Un compilatore aggiornato ti avvertirà di una dichiarazione mancante di malloc.
n. 'pronomi' m.

55
@nm Ok. Penso che sia male presumere che chiunque legga qui abbia un compilatore particolare. Inoltre, poiché C11 l'intero concetto di "funzione implicita" è sparito, non lo sapevo. Tuttavia, non vedo il punto di aggiungere un cast inutile. Fai anche int x = (int) 12;solo per chiarire le cose?
Rilassati l'

22
@nm se il cast esplicito di un puntatore vuoto "ha aiutato" a risolvere un bug, è molto probabile che tu abbia riscontrato un comportamento indefinito, il che significherebbe che il programma in questione probabilmente ha un bug molto peggio, ancora da scoprire, che non hai ancora incontrato. E un giorno, in una fredda sera d'inverno, tornerai a casa dal lavoro per trovare la tua pagina GitHub piena di segnalazioni di problemi che si lamentano dei demoni che volano fuori dal naso degli utenti
Braden Best

12
@unwind Anche io sono d'accordo con te, (int)12non è paragonabile. 12 è un int, il cast semplicemente non fa nulla. Il valore di retval di malloc()è void *, non il tipo di puntatore su cui è stato eseguito il cast. (Se non lo è void *. Quindi l'analogia (int)12sarebbe (void*)malloc(…)quello che nessuno sta discutendo.)
Amin Negm-Awad

376

In C, non è necessario eseguire il cast del valore restituito di malloc. Il puntatore al vuoto restituito da mallocviene automaticamente convertito nel tipo corretto. Tuttavia, se si desidera che il codice venga compilato con un compilatore C ++, è necessario un cast. Un'alternativa preferita nella comunità è usare quanto segue:

int *sieve = malloc(sizeof *sieve * length);

che ti libera inoltre dal doverti preoccupare di cambiare il lato destro dell'espressione se mai cambi il tipo di sieve.

I cast sono cattivi, come hanno sottolineato le persone. Soprattutto cast puntatore.


71
@MAKZ Direi che malloc(length * sizeof *sieve)sembra che sizeofsia una variabile - quindi penso che malloc(length * sizeof(*sieve))sia più leggibile.
Michael Anderson,

21
E malloc(length * (sizeof *sieve))ancora più leggibile. A PARER MIO.
Toby Speight,

19
@Michael Anderson ()problema a parte, nota che il tuo stile suggerito ha cambiato l'ordine., Considera quando il conteggio degli elementi è calcolato come length*width, mantenendo il sizeofprimo in questo caso assicurati che la moltiplicazione sia fatta almeno con la size_tmatematica. Confronta malloc(sizeof( *ptr) * length * width)vs. malloc(length * width * sizeof (*ptr))- il 2 ° potrebbe traboccare length*widthquando width,lengthsono tipi più piccoli size_t.
chux - Ripristina Monica il

3
@chux non è ovvio, ma la risposta è stata modificata in modo che il mio commento sia meno pertinente - il suggerimento originale eramalloc(sizeof *sieve * length)
Michael Anderson,

12
C non è C ++. Fingere che lo siano alla fine porterà a confusione e tristezza. Se stai usando C ++, anche un cast in stile C è male (a meno che tu non stia usando un compilatore C ++ molto vecchio). E static_cast>()(o reinterpret_cast<>()) non è compatibile con nessun dialetto di C.
David C.,

349

Si fa lanci, in quanto:

  • Rende il tuo codice più portabile tra C e C ++ e, come dimostra l'esperienza SO, molti programmatori affermano che stanno scrivendo in C quando stanno davvero scrivendo in C ++ (o C più estensioni di compilatore locali).
  • Non farlo può nascondere un errore : annotare tutti gli esempi SO di confusione su quando scrivere type *contro type **.
  • L'idea che ti impedisce di notare che non sei riuscito ad #includeun file di intestazione appropriato manca la foresta per gli alberi . È come dire "non preoccuparti del fatto che non sei riuscito a chiedere al compilatore di lamentarti di non aver visto i prototipi - che fastidioso stdlib.h è la cosa REALE importante da ricordare!"
  • Forza un controllo incrociato cognitivo in più . 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 può controllare è spesso un'ottima idea. In effetti, ecco cos'è un'asserzione, e questo uso del cast è un'asserzione. Le asserzioni sono ancora la tecnica più generale che abbiamo per ottenere il codice corretto, dal momento che Turing ha avuto l'idea così tanti anni fa.

39
@ulidtko Nel caso non lo sapessi, è possibile scrivere codice che compila sia come C sia come C ++. In effetti la maggior parte dei file di intestazione sono così e spesso contengono codice (macro e funzioni incorporate). Avere un file .c/ .cppda compilare in quanto entrambi non è utile molto spesso, ma un caso è l'aggiunta del throwsupporto C ++ quando compilato con il compilatore C ++ (ma return -1;quando compilato con il compilatore C o qualsiasi altra cosa).
hyde,

37
Se qualcuno avesse chiamate malloc in linea in un header non sarei impressionato, #ifdef __cplusplus ed extern "C" {} sono per questo lavoro, non aggiungendo cast extra.
paulm

15
Bene, il punto 1 è irrilevante, dal momento che C! = C ++, anche gli altri punti sono banali, se usi la variabile nella tua mallocchiamata: char **foo = malloc(3*sizeof(*foo));se abbastanza a prova: 3 puntatori a puntatori a caratteri. quindi eseguire il ciclo e fare foo[i] = calloc(101, sizeof(*(foo[i])));. Alloca array di 101 caratteri, inizializzato ordinatamente a zero. Nessun cast necessario. cambia la dichiarazione unsigned charo qualsiasi altro tipo, del resto, e sei ancora bravo
Elias Van Ootegem,

34
Quando ho pensato l'ho capito, ecco che arriva! Risposta fantastica. È la prima volta qui in StackOverflow che faccio +1 su due risposte opposte! +1 No, non lanciare, e +1 Sì, lanci! LOL. Ragazzi siete fantastici. E per me e i miei studenti, ho deciso: faccio il cast. Il tipo di errori che gli studenti commettono vengono individuati più facilmente durante il casting.
Dr Beco,

15
@Leushenko: ripeterti in un modo che non può essere convalidato dalla macchina o dall'ispezione locale è un male. Ripetersi in modi che possono essere validati con tali mezzi è meno male. Dato struct Zebra *p; ... p=malloc(sizeof struct Zebra);, il malloc non può evitare di duplicare le informazioni sul tipo di p, ma né il compilatore né l'ispezione del codice locale avrebbero rilevato alcun problema se un tipo è cambiato ma l'altro no. Cambia il codice in p=(struct Zebra*)malloc(sizeof struct Zebra);e il compilatore scricchiolerà se il tipo di cast non corrisponde p, e l' ispezione locale rivelerà ...
supercat

170

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 utilizzare 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 ++.


17
Dato che stai usando comunque una macro, perché non la usi newnella definizione di C ++?
Hosam Aly,

63
Perché non c'è motivo di farlo. È principalmente per i programmi C compilati con un compilatore C ++. Se hai intenzione di usare "nuovo", l'unica cosa che ottieni sono i problemi. È quindi necessario anche una macro gratuitamente. E hai bisogno di una macro per liberare un array, una differenziazione che non esiste in C.
quinmars

8
Per non parlare del fatto che non sei tu che libera la memoria ma forse una libreria C che stai utilizzando, ecc. Molti possibili problemi senza alcun guadagno.
Quinmars,

86
@Hosam: Sì, lo è sicuramente. Se usi newdevi usare deletee se usi malloc()devi farlo free(). Non mescolarli mai.
Graeme Perrow,

17
Se uno adotterà questo approccio, chiamare la macro NEWè probabilmente una cattiva idea poiché la risorsa non viene mai restituita usando delete(o DELETE), quindi stai mescolando il tuo vocabolario. Invece, nominarlo MALLOC, o meglio CALLOCin questo caso, avrebbe più senso.
Mah

139

Da Wikipedia :

Vantaggi del casting

  • Includere il cast può consentire a un programma o una funzione C di essere compilato come C ++.

  • Il cast consente versioni pre-1989 di malloc che originariamente restituivano un carattere *.

  • Il cast può aiutare lo sviluppatore a identificare incoerenze nel dimensionamento del tipo nel caso in cui il tipo di puntatore di destinazione cambi, in particolare se il puntatore viene dichiarato lontano dalla chiamata malloc () (sebbene i compilatori moderni e gli analizzatori statici possano avvisare di tale comportamento senza richiedere il cast).

Svantaggi del casting

  • Secondo lo standard ANSI C, il cast è ridondante.

  • L'aggiunta del cast potrebbe non riuscire a includere l'intestazione stdlib.h, in cui si trova il prototipo di 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 in bit mentre la funzione attualmente definita restituisce un valore a 64 bit. A seconda delle convenzioni di chiamata e del layout della memoria, ciò può provocare la distruzione dello stack. È meno probabile che questo problema passi inosservato nei compilatori moderni, poiché producono in modo uniforme avvisi che è stata utilizzata una funzione non dichiarata, quindi verrà comunque visualizzato un avviso. Ad esempio, il comportamento predefinito di GCC è mostrare un avviso che legge "dichiarazione implicita incompatibile della funzione incorporata" indipendentemente dal fatto che il cast sia presente o meno.

  • Se il tipo di puntatore viene modificato durante la sua dichiarazione, è possibile che sia necessario modificare tutte le righe in cui viene chiamato e lanciato cast.

Sebbene malloc senza casting sia il metodo preferito e lo scelga la maggior parte dei programmatori esperti , dovresti usare quello che ti piace conoscere i problemi.

vale a dire: se è necessario compilare il programma C come C ++ (sebbene sia un linguaggio separato) è necessario trasmettere il risultato dell'uso malloc.


1
Che cosa significa "Il cast può aiutare lo sviluppatore a identificare incoerenze nel dimensionamento del tipo nel caso in cui il tipo di puntatore di destinazione cambi, in particolare se il puntatore viene dichiarato lontano dalla malloc()chiamata "? Potresti fare un esempio?
Spikatrix,

3
@CoolGuy: vedi un commento precedente su un'altra risposta . Ma nota che l' p = malloc(sizeof(*p) * count)idioma rileva automaticamente le modifiche nel tipo, quindi non devi ricevere avvisi e andare a cambiare nulla. Quindi questo non è un vero vantaggio rispetto alla migliore alternativa per il non casting.
Peter Cordes,

7
Questa è la risposta corretta: ci sono pro e contro, e si riduce a una questione di gusti (a meno che il codice non debba essere compilato come C ++, quindi il cast è obbligatorio).
Peter - Ripristina Monica il

3
Il punto 3 è controverso, poiché se il tipo di puntatore viene modificato nella sua dichiarazione, si dovrebbe controllare ogni istanza di malloc, realloc e free inolving quel tipo. Il casting ti costringerà a fare proprio questo.
Michaël Roy,

104

In C puoi implicitamente convertire un voidpuntatore in qualsiasi altro tipo di puntatore, quindi non è necessario un cast. L'uso di uno può suggerire all'osservatore casuale che c'è qualche motivo per cui uno è necessario, il che può essere fuorviante.


100

Non lanci il risultato di malloc, perché in questo modo aggiungi disordine inutile al tuo codice.

Il motivo più comune per cui le persone lanciano il risultato di malloc è perché non sono sicuri su come funzioni il linguaggio C. Questo è un segnale di avvertimento: se non sai come funziona un meccanismo linguistico particolare, allora non indovinare. Cerca o chiedi su Stack Overflow.

Alcuni commenti:

  • Un puntatore vuoto 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 non malloc (). E non dovresti mai compilare il 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, otterrai un errore compilatore / linker a riguardo. Quindi, se hai dimenticato di includere <stdlib.h>questo non è un problema, non sarai in grado di costruire il tuo programma.

  • Sui compilatori antichi che seguono una versione dello standard che ha più di 25 anni, dimenticare di includere <stdlib.h>comporterebbe comportamenti pericolosi. Perché in quello standard antico, le funzioni senza un prototipo visibile hanno convertito implicitamente il tipo restituito in int. Lanciare il risultato da malloc in modo esplicito nasconderebbe questo bug.

    Ma questo è davvero un problema. Non stai usando un computer di 25 anni, quindi perché dovresti usare un compilatore di 25 anni?


9
"disordine inutile" è iperbole sprezzante che tende a deragliare ogni possibilità di convincere chiunque non sia già d'accordo con te. Un cast certamente non ha senso; Le risposte di Ron Burk e Kaz sostengono argomenti a favore del casting con cui sono molto d'accordo. Se queste preoccupazioni pesino più di quelle che menzioni è una domanda ragionevole da porre. Per me, le tue preoccupazioni sembrano relativamente minori rispetto alle loro.
Don Hatch,

"Un puntatore vuoto può essere convertito in / da qualsiasi altro tipo di puntatore senza un cast esplicito" non è supportato da 6.3.2.3. Forse stai pensando a "puntatore a qualsiasi tipo di oggetto"? "puntatore vuoto" e "puntatore a una funzione" non sono così facilmente convertibili.
chux - Ripristina Monica il

In effetti il ​​riferimento era incompleto. La parte rilevante per "l'implicazione" è la regola del semplice incarico 6.5.16.1. "un operando è un puntatore a un tipo di oggetto e l'altro è un puntatore a una versione qualificata o non qualificata del vuoto". Ho aggiunto questo riferimento alla risposta per completezza.
Lundin,

91

In C ottieni una conversione implicita da void *qualsiasi altro puntatore (dati).


6
@Jens: OK, forse la formulazione più corretta è "conversione implicita". Come l'uso della variabile integrale nell'espressione in virgola mobile.
EFraim

@EFraim Ciò si tradurrebbe in realtà in un cast, e implicito in quello.
Fisico pazzo,

71

Casting del valore restituito da malloc() non è necessario ora, ma vorrei aggiungere un punto che sembra che nessuno abbia sottolineato:

Nei tempi antichi, cioè prima che ANSI C fornisse il void *tipo generico di puntatori,char * è il tipo per tale utilizzo. In tal caso, il cast può chiudere gli avvisi del compilatore.

Riferimento: C FAQ


2
Chiudere gli avvisi del compilatore è una cattiva idea.
Albert van der Horst,

8
@AlbertvanderHorst Non se lo fai risolvendo il problema esatto, l'avviso è lì per avvisarti.
Dan Bechard,

@Dan. Se risolvendo il problema esatto si intende la riscrittura di una subroutine per restituire i moderni tipi ANSI C anziché char *, sono d'accordo. Non lo definirei chiudere il compilatore. Non cedere ai manager che insistono sul fatto che non ci siano avvisi del compilatore, invece di usarli per ogni ricompilazione per trovare possibili problemi. Groetjes Albert
Albert van der Horst,

53

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 usato solo per essere assolutamente specifico e per convincere gli studenti a essere assolutamente specifici. Fondamentalmente il casting non cambierà nulla nel modo in cui funziona, fa esattamente ciò che dice, alloca la memoria e il casting non ha effetto, ottieni la stessa memoria e anche se lo cast su qualcos'altro per errore (e in qualche modo elude il compilatore errori) C accederà allo stesso modo.

Modifica: il casting ha un certo punto. Quando si utilizza la notazione di array, il codice generato deve sapere quante posizioni di memoria deve avanzare per raggiungere l'inizio dell'elemento successivo, ciò si ottiene attraverso il cast. In questo modo sai che per un doppio vai avanti di 8 byte mentre per un int vai a 4, e così via. Pertanto, non ha alcun effetto se si utilizza la notazione del puntatore, nella notazione di matrice diventa necessario.


3
Ad eccezione di quanto già menzionato, il cast potrebbe nascondere bug e rendere il codice più difficile da analizzare per il compilatore o l'analizzatore statico.
Lundin,

2
"Essenzialmente il casting non cambierà nulla nel modo in cui funziona". Il cast al tipo corrispondente non dovrebbe cambiare nulla, ma il tipo di var dovrebbe cambiare e il cast non dovrebbe più corrispondere, potrebbero sorgere problemi? IWO, il tipo cast e var dovrebbero essere sincronizzati - il doppio dei lavori di manutenzione.
chux - Ripristina Monica il

Vedo perché i prof preferiscono il casting. Il casting può essere utile da un punto di vista educativo in cui trasmette alle informazioni dell'istruttore e non è necessario mantenere il codice dello studente - il suo codice da buttare via. Tuttavia, dal punto di vista della codifica, della revisione tra pari e della manutenzione , p = malloc(sizeof *p * n);è così semplice e migliore.
chux - Ripristina Monica il

53

Non è obbligatorio eseguire il cast dei risultati di malloc, poiché restituisce void*, e void*può essere indicato qualsiasi tipo di dati.


34

Un puntatore vuoto è un puntatore oggetto generico e C supporta la conversione implicita da un tipo di puntatore vuoto ad altri tipi, quindi non è necessario scriverlo esplicitamente.

Tuttavia, se si desidera che lo stesso codice funzioni perfettamente compatibile su una piattaforma C ++, che non supporta la conversione implicita, è necessario eseguire il typecasting, quindi tutto dipende dall'usabilità.


2
Non è un normale caso di utilizzo compilare una singola fonte come C e C ++ (al contrario, diciamo, di usare un file header contenente dichiarazioni per collegare insieme codice C e C ++). L'uso malloce gli amici in C ++ è un buon segnale di avvertimento che merita particolare attenzione (o riscrittura in C).
Toby Speight,

1
"Un puntatore vuoto è un puntatore generico" -> "Un puntatore vuoto è un puntatore oggetto generico ". Le dimensioni dei puntatori a funzione possono superare void *, pertanto void *non è sufficiente per memorizzare bene un puntatore a funzione.
chux - Ripristina Monica il

la mia intenzione di quella linea era la stessa, ma grazie @chux per il suggerimento.
Endeavour

33

Questo è ciò che dice il manuale di riferimento della libreria GNU C :

Puoi archiviare il risultato mallocin qualsiasi variabile di puntatore senza cast, perché ISO C converte automaticamente il tipo void *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. tradizionale

E in effetti lo standard ISO C11 (p347) lo dice:

Il puntatore restituito se l'allocazione ha esito positivo è 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 o a una matrice di tali oggetti nello spazio allocato (fino a quando il lo spazio è esplicitamente deallocato)


31

Il tipo restituito è nullo *, che può essere trasmesso al tipo di puntatore di dati desiderato per essere dichiarabile.


1
void* può essere trasmesso al tipo desiderato, ma non è necessario in quanto verrà convertito automaticamente. Quindi il cast non è necessario, e in effetti indesiderabile per i motivi menzionati nelle risposte ad alto punteggio.
Toby Speight,

ma solo se devi dereferenziarlo "al volo", se invece crei una variabile, questa verrà convertita in modo sicuro e automatico nel tipo effettivo della variabile, senza casting (in C).
Ferrarezi,

28

Nel linguaggio C, un puntatore vuoto può essere assegnato a qualsiasi puntatore, motivo per cui non si dovrebbe usare un cast di tipo. Se si desidera un'allocazione "tipo sicuro", 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 sul posto puoi semplicemente dire

NEW_ARRAY(sieve, length);

Per le matrici non dinamiche, la terza macro di funzioni indispensabili è

#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++) {
   ...
}

"Un puntatore vuoto può essere assegnato a qualsiasi puntatore oggetto " I puntatori funzione sono un altro problema, sebbene non malloc()uno.
chux - Ripristina Monica il

Assegnare un void*puntatore a / da un puntatore a funzione può perdere informazioni, quindi "un puntatore vuoto può essere assegnato a qualsiasi puntatore" è un problema in questi casi. L'assegnazione di un void*, da malloc() a qualsiasi puntatore a oggetti non è tuttavia un problema.
chux - Ripristina Monica il

Il docommento del ciclo si riferiva a macro che coinvolgono un ciclo ma che si sta ancora chiedendo alla domanda del titolo. Rimozione di quel commento. Prenderò questo anche più tardi.
chux - Ripristina Monica il

27

Dipende dal linguaggio di programmazione e dal compilatore. Se si utilizza mallocin C, non è necessario digitare cast, poiché verrà automaticamente digitato cast. Tuttavia, se si utilizza C ++, è necessario digitare cast perché mallocrestituirà un void*tipo.


1
La funzione malloc restituisce un puntatore vuoto anche in C ma le regole del linguaggio sono diverse da C ++.
August Karlstrom,

16

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 anziani che mi è stato richiesto di usare. Spesso aziende e manager adottano un approccio ultra-conservativo per cambiare compilatore e non lo faranno nemmeno testare 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 stai programmando devi coprire le tue basi e, sfortunatamente, lanciare malloc è una buona abitudine se non puoi controllare quale compilatore può essere applicato al tuo codice.

Vorrei anche suggerire che molte organizzazioni applicano uno standard di codifica proprio e quello dovrebbe essere il metodo che le persone seguono se definito. In assenza di una guida esplicita, tendo a cercare la maggior parte delle probabilità di compilare dappertutto, piuttosto che una rigida aderenza a uno standard.

L'argomento secondo cui non è necessario secondo gli standard attuali è abbastanza valido. Ma tale argomento omette le funzionalità del mondo reale. Non codifichiamo in un mondo governato esclusivamente dallo standard del giorno, ma dalle praticità di quello che mi piace chiamare "campo della realtà della gestione locale". E questo è piegato e contorto più di quanto lo sia mai stato lo spazio. :-)

YMMV.

Tendo a pensare al casting di 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!).


15

Ho inserito il cast semplicemente per mostrare la disapprovazione del brutto buco nel sistema dei tipi, che consente a codice come il seguente frammento di compilare senza diagnostica, anche se non vengono utilizzati cast per determinare la conversione errata:

double d;
void *p = &d;
int *q = p;

Vorrei che non esistesse (e non lo sia in C ++) e quindi lancio. Rappresenta il mio gusto e la mia politica di programmazione. Non sto solo lanciando un puntatore, ma in effetti, lancio un voto e scacciando demoni di stupidità . Se non riesco davvero a scacciare la stupidità , almeno lasciami esprimere il desiderio di farlo con un gesto di protesta.

In effetti, una buona pratica è quella di avvolgere malloc(e amici) con le funzioni che ritornano unsigned char *, e sostanzialmente non usare mai void *nel tuo codice. Se hai bisogno di un generico puntatore a qualsiasi oggetto, usa un char *o unsigned char *e hai i cast in entrambe le direzioni. L'unico rilassamento che può essere concesso, forse, è usare funzioni come memsete memcpysenza calchi.

Sul tema della fusione e la compatibilità C ++, se si scrive il codice in modo che si compila sia come C e C ++ (nel qual caso si deve lanciare il valore restituito mallocquando si assegna a qualcosa di diverso void *), si può fare un molto utile cosa per te: puoi usare le macro per il cast che si traducono in cast in stile C ++ durante la compilazione come C ++, ma riducono a un cast C durante la compilazione 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 grepricerca nella 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 si utilizza strip_qualsolo per rimuovere un consto volatile, ma il programma cambia in modo tale da coinvolgere una conversione di tipo, si otterrà una diagnostica e sarà necessario utilizzare 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 funzionalità: 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
         effetti indesiderati e molto più facile da cercare.

Se il tuo codice C viene compilato come C ++, puoi utilizzare questa -Wold-style-castopzione per scoprire tutte le occorrenze della (type)sintassi del casting che possono insinuarsi nel codice e dare seguito a questi diagnostici sostituendolo con una scelta appropriata tra le macro sopra (o un combinazione, se necessario).

Questo trattamento delle conversioni è la più grande giustificazione tecnica autonoma per lavorare in una "C pulita": il dialetto C e C ++ combinato, che a sua volta giustifica tecnicamente il valore di ritorno di malloc.


Come altri hanno sottolineato, di solito consiglierei di non mescolare codice C e C ++. Tuttavia, se hai buone ragioni per farlo, le macro potrebbero essere utili.
Phil1970,

@ Phil1970 È tutto scritto in un dialetto coerente, che risulta essere portatile per i compilatori C e C ++, e sfrutta alcune funzionalità di C ++. Deve essere tutto compilato come C ++, oppure tutto compilato come C.
Kaz

Cioè quello che stavo cercando di dire nel commento precedente è che non c'è miscelazione di C e C ++. L'intento è che il codice sia tutto compilato come C o tutto compilato come C ++.
Kaz,

15

La cosa migliore da fare quando si programma in C ogni volta che è possibile:

  1. Fai compilare il tuo programma attraverso un compilatore C con tutti gli avvisi attivati -Walle correggi tutti gli errori e gli avvisi
  2. Assicurarsi che non vi siano variabili dichiarate come auto
  3. Quindi compilarlo usando un compilatore C ++ con -Walle -std=c++11. Correggi tutti gli errori e gli avvisi.
  4. Ora compilare nuovamente usando il compilatore C. Il programma dovrebbe ora essere compilato senza alcun preavviso e contenere meno bug.

Questa procedura consente di sfruttare il controllo rigoroso del tipo C ++, riducendo così il numero di bug. In particolare, questa procedura ti obbliga a includere stdlib.ho otterrai

malloc non è stato dichiarato in questo ambito

e ti costringe anche a lanciare il risultato malloco otterrai

conversione non valida da void*aT*

o qualunque sia il tuo tipo di target.

Gli unici vantaggi della scrittura in C anziché in C ++ che posso trovare sono

  1. C ha un ABI ben specificato
  2. Il C ++ può generare più codice [eccezioni, RTTI, modelli, polimorfismo di runtime ]

Si noti che, nel caso ideale, i secondi contro dovrebbero scomparire quando si usa il sottoinsieme comune a C insieme alla caratteristica polimorfica statica .

Per coloro che trovano inopportune regole rigorose in C ++, possiamo usare la funzione C ++ 11 con tipo dedotto

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

18
Utilizzare un compilatore C per il codice C. Utilizzare un compilatore C ++ per il codice C ++. Nessun se, nessun ma. Riscrivere il tuo codice C in C ++ è un'altra cosa, e può - o non valere - valere il tempo e i rischi.
Toby Speight,

2
Vorrei aggiungere i consigli di @TobySpeight: se è necessario utilizzare il codice C in un progetto C ++, di solito è possibile compilare il codice C come C (ad esempio gcc -c c_code.c), il codice C ++ come C ++ (ad esempio g++ -c cpp_code.cpp), quindi collegarli insieme (ad es. gcc c_code.o cpp_code.oo viceversa a seconda delle dipendenze del progetto). Ora non ci dovrebbero essere motivi per privarti delle belle caratteristiche di entrambe le lingue ...
autistico,

1
@ user877329 È un'alternativa più sensata all'aggiunta scrupolosa di cast al codice che riduce la leggibilità del codice, solo per essere "compatibile con C ++".
autistico,

1
Probabilmente il vantaggio principale in questo contesto è che C ti consente di scrivere p = malloc(sizeof(*p));, che non ha bisogno di cambiare in primo luogo se si pcambia con un nome di tipo diverso. Il "vantaggio" proposto del cast è che si ottiene un errore di compilazione se pè del tipo sbagliato, ma è ancora meglio se funziona.
Peter Cordes,

1
Vorrei menzionare che la scrittura in C può essere necessaria quando si prendono di mira piattaforme prive di compilatori C ++ adeguati. Eccezioni e modelli sono funzionalità che in genere aiutano c ++ a generare codice più piccolo e / o più efficiente mentre il polimorfismo di runtime in C ++ è per lo più equivalente a C.
user7860670

15

No, non lanci il risultato di malloc().

In generale, nonvoid * si esegue il cast da o verso .

Un motivo tipico fornito per non farlo è che il mancato rispetto #include <stdlib.h>potrebbe passare inosservato. Questo non è più un problema da molto tempo poiché C99 ha reso dichiarazioni di funzioni implicite illegali le , quindi se il tuo compilatore è conforme almeno a C99, riceverai un messaggio diagnostico.

Ma c'è una ragione molto più forte per non introdurre lanci puntatori non necessari:

In C, un cast 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 con segno o senza segno corrispondente al tipo effettivo dell'oggetto,
- un tipo che è il tipo con segno o non firmato corrispondente a una versione qualificata del tipo effettivo dell'oggetto,
- un tipo aggregato o unione che include uno dei suddetti tipi tra i suoi membri (incluso, ricorsivamente, un membro di un'unione sottaggregata o contenuta), oppure
- un tipo di carattere.

Questa è anche nota come regola di aliasing rigorosa . Quindi il codice seguente è un comportamento indefinito :

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. Pertanto, qualsiasi occorrenza di un cast di puntatori nel codice è un luogo in cui è necessario ricontrollare la sua validità . Pertanto, non si scrive mai un cast puntatore non necessario.

tl; dr

In poche parole: poiché in C, qualsiasi occorrenza di un cast di puntatori 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:

  • Esistono casi in cui è effettivamente necessario un cast void *, ad esempio se si desidera 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 puntatori è piuttosto comune (e corretto) quando si tratta di oggetti di classi derivate. Pertanto, è logico che in C ++, la conversione da e void *è non implicito. C ++ ha un intero set di diversi tipi di casting.


1
Nei tuoi esempi eviti il ​​vuoto *. c'è una differenza tra cast da double * a int * e viceversa. malloc restituisce pointel allineato al tipo standard più grande, quindi non ci sono regole di aliasing infrante anche se qualcuno lancia questo puntatore allineato ad un altro tipo.
P__J__,

Aliasing ha non nulla a che fare con l'allineamento e per il resto del tuo commento - ovviamente non hai capito.

@PeterJ: per ogni evenienza, il punto è evitare un cast puntatore non necessario, quindi non lo fa sembra un pezzo di codice a cui devi prestare particolare attenzione.

Il rigoroso problema di aliasing non ha davvero nulla a che fare con i puntatori vuoti. Per ottenere bug causati da violazioni rigorose dell'aliasing, è necessario de-referenziare i dati puntati. E poiché non è possibile rimandare a un puntatore vuoto, tali bug non sono per definizione correlati al puntatore vuoto ma a qualcos'altro.
Lundin,

Piuttosto, dovresti fare una regola per vietare tutti i cast di puntatori. Ma allora come scriveresti cose come le routine di serializzazione e la programmazione relativa all'hardware? Cose che sono la forza di C. Tali lanci vanno bene se sai cosa stai facendo.
Lundin,

15

Preferisco fare il cast, ma non manualmente. Il mio preferito è usare g_neweg_new0 macro da glib. Se non si usa glib, aggiungerei macro simili. Queste macro riducono la duplicazione del codice senza compromettere la sicurezza del tipo. Se si sbaglia il tipo, si otterrebbe un cast implicito tra puntatori non nulli, che causerebbe un avviso (errore in C ++). Se si dimentica di includere l'intestazione che definisce g_newe g_new0, si otterrebbe un errore. g_newed g_new0entrambi accettano gli stessi argomenti, diversamente da mallocquello che richiede meno argomenti di calloc. Basta aggiungere 0per ottenere la memoria zero inizializzata. Il codice può essere compilato con un compilatore C ++ senza modifiche.


12

Il cast è solo per C ++ non C. Nel caso in cui si stia utilizzando un compilatore C ++, è meglio modificarlo in compilatore C.


9

Il concetto alla base del puntatore void è che può essere trasmesso a qualsiasi tipo di dati, motivo per cui malloc restituisce void. Inoltre, devi essere consapevole del typecasting automatico. Quindi non è obbligatorio lanciare il puntatore anche se è necessario farlo. Aiuta a mantenere pulito il codice e aiuta il debug


11
" Non è obbligatorio - anche se devi farlo " - Penso che ci sia una contraddizione lì!
Toby Speight,

5
Penso che dovresti leggere questo post a qualcuno e vedere se capiscono cosa stai cercando di dire. Quindi riscriverlo, chiarendo cosa vuoi dire. Non riesco davvero a capire quale sia la tua risposta.
Bill Woodger,

9

Un puntatore vuoto è un puntatore generico e C supporta la conversione implicita da un tipo di puntatore vuoto ad altri tipi, quindi non è necessario scriverlo esplicitamente.

Tuttavia, se si desidera che lo stesso codice funzioni perfettamente compatibile su una piattaforma C ++, che non supporta la conversione implicita, è necessario eseguire il typecasting, quindi tutto dipende dall'usabilità.


9
  1. Come altri hanno affermato, non è necessario per C, ma per C ++.

  2. Includere il cast può consentire a un programma o una funzione C di essere compilato come C ++.

  3. In C non è necessario, poiché void * viene promosso automaticamente e in modo sicuro su qualsiasi altro tipo di puntatore.

  4. 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 un arresto fino a molto tempo dopo in una parte completamente diversa del codice).

    Perché stdlib.h contiene il prototipo di malloc 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.


7

Il casting di malloc non è necessario in C ma obbligatorio in C ++.

Il cast 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 hai dimenticato di includere <stdlib.h>. Ciò può causare arresti anomali.
  • Se puntatori e numeri interi hanno dimensioni diverse, allora stai nascondendo un avviso lanciando e potresti perdere bit dell'indirizzo restituito.
  • Se il tipo di puntatore viene modificato durante la sua dichiarazione, potrebbe essere necessario modificare tutte le linee in cui mallocviene chiamato e trasmesso.

D'altro canto, il casting può aumentare la portabilità del programma. cioè, consente a un programma o funzione C di essere compilato come C ++.


0

Per me, il ritorno a casa e la conclusione qui è che il casting mallocin C NON è assolutamente necessario, ma se si esegue comunque il cast, non influirà in mallocquanto mallocvi assegnerà ancora lo spazio di memoria benedetta richiesto. Un altro da portare a casa è il motivo o uno dei motivi per cui le persone eseguono il casting e ciò consente loro di compilare lo stesso programma in C o C ++.

Potrebbero esserci altri motivi, ma altri motivi, quasi certamente, ti metterebbero in guai seri prima o poi.


0

È possibile, ma non è necessario eseguire il cast in C. È necessario eseguire il cast se tale codice viene compilato come C ++.

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.