casting esempio - Do il risultato di malloc?




free array (22)

In questa domanda , qualcuno ha suggerito in un comment che non avrei dovuto esprimere il risultato di malloc , ad es

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

piuttosto che:

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

Perché dovrebbe essere così?


Answers

Da Wikipedia

Vantaggi del casting

  • Includere il cast potrebbe consentire a un programma o una funzione C di compilare come C ++.

  • Il cast consente versioni di malloc precedenti al 1989 che originariamente restituivano un char *.

  • Il cast può aiutare lo sviluppatore a identificare incongruenze nel dimensionamento del tipo in caso di cambiamento del tipo di puntatore di destinazione, in particolare se il puntatore è dichiarato lontano dalla chiamata malloc (), anche se i moderni compilatori e analizzatori statici possono avvisare su tale comportamento senza richiedere il cast.

Svantaggi del casting

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

  • L'aggiunta del cast può mascherare l'errore di includere l'intestazione stdlib.h , in cui viene trovato il prototipo per malloc. In assenza di un prototipo per malloc, lo standard richiede che il compilatore C presuma che malloc restituisca un int. Se non c'è cast, viene emesso un avviso quando questo intero è 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 lunghi e puntatori sono 64-bit e int è a 32-bit), questo errore può effettivamente comportare un comportamento indefinito, poiché il malloc implicitamente dichiarato 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 di memoria, ciò potrebbe causare problemi di stacking. È meno probabile che questo problema passi inosservato nei moderni compilatori, poiché generano in modo uniforme avvertimenti che è stata utilizzata una funzione non dichiarata, pertanto verrà visualizzato un avviso. Ad esempio, il comportamento predefinito di GCC è di mostrare un avviso che legge "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, è anche possibile che sia necessario modificare tutte le righe in cui viene chiamato e lanciato cast di malloc.

Sebbene malloc senza casting sia il metodo preferito e la maggior parte dei programmatori esperti lo scelga , dovresti usare quello che preferisci avere a conoscenza dei problemi.

es .: Se hai bisogno di compilare il programma C come C ++ (anche se sono lingue separate) dovresti usare malloc con il cast.


La conversione del valore restituito da malloc() non è necessaria ora, ma vorrei aggiungere un punto che sembra nessuno ha sottolineato:

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

Riferimento: C FAQ


Fai il cast perché:

  • Rende il tuo codice più portabile tra C e C ++, e come mostra l'esperienza SO, molti programmatori sostengono di scrivere in C quando stanno davvero scrivendo in C ++ (o C oltre alle estensioni del compilatore locale).
  • Non riuscendo a farlo può nascondere un errore : nota tutti gli esempi SO di confusione quando scrivere type * contro type ** .
  • L'idea che ti impedisce di notare che hai fallito #include un file di intestazione appropriato, manca la foresta per gli alberi . È come dire "non ti preoccupare del fatto che non hai chiesto al compilatore di lamentarsi del fatto di non vedere i prototipi - che fastidioso stdlib.h è la vera cosa importante da ricordare!"
  • Costringe un ulteriore controllo incrociato cognitivo . 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 bug di malloc() vengono catturati molto più velocemente quando c'è un cast. Come per le asserzioni, le annotazioni che rivelano l'intenzione diminuiscono i bug.
  • Ripetersi in un modo che la macchina può controllare è spesso una grande idea. In realtà, questo è ciò che è un'affermazione, 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 di tanti anni fa.

No, non si esegue il cast di risultati malloc().

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

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

Ma c'è una ragione molto più forte per non introdurre cast di puntatori inutili:

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 firmato o senza segno corrispondente al tipo effettivo dell'oggetto,
- un tipo che è il tipo firmato o senza segno corrispondente a una versione qualificata del tipo effettivo dell'oggetto,
- un tipo di aggregato o unione che include uno dei tipi sopra menzionati tra i suoi membri (incluso, in modo ricorsivo, un membro di un'unione subaggregata o contenuta), o
- un tipo di carattere.

Questa è anche nota come regola di aliasing rigorosa . Quindi il seguente codice è 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 lanciare puntatori, ma data la rigida regola di aliasing , bisogna stare molto attenti con esso. Quindi, qualsiasi occorrenza di un puntatore lanciato nel tuo codice è un luogo che devi ricontrollare per 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 si dovrebbero mai scrivere cast di puntatori inutili .

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. I tipi di puntatori del cast sono in qualche modo comuni (e corretti) quando si gestiscono oggetti di classi derivate. Pertanto, ha senso che in C ++, la conversione da e verso nonvoid * è implicita. Il C ++ ha un intero set di differenti gusti di casting.


Le persone abituate a GCC e Clang sono viziate. Non è poi così bello là fuori.

Sono stato piuttosto orripilato nel corso degli anni dai compilatori invecchiati in modo sconcertante che mi è stato richiesto di usare. Spesso aziende e 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 l'ottimizzazione del codice) funzionerà nel loro sistema. La realtà pratica per gli sviluppatori che lavorano è che quando si sta codificando è necessario coprire le basi e, sfortunatamente, lanciare mallocs è una buona abitudine se non si riesce a controllare quale compilatore può essere applicato al codice.

Suggerirei inoltre che molte organizzazioni applichino uno standard di codifica proprio e che quello dovrebbe essere il metodo seguito dalle persone se viene definito. In assenza di una guida esplicita, io tendo ad andare per la maggior parte delle probabilità di compilare ovunque, piuttosto che seguire pedissequamente uno standard.

L'argomento secondo cui non è necessario secondo gli standard attuali è abbastanza valido. Ma questa argomentazione omette gli aspetti pratici del mondo reale. Non codifichiamo in un mondo governato esclusivamente dallo standard del giorno, ma dagli aspetti pratici di ciò che mi piace definire "il campo della realtà della gestione locale". E questo è piegato e distorto più di quanto lo spazio non sia mai stato. :-)

YMMV.

Tendo a pensare di lanciare malloc come operazione difensiva. Non bello, non perfetto, ma generalmente sicuro. (Onestamente, se non hai incluso stdlib.h, hai molti più problemi che lanciare malloc!).


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 allocare "sicuro", posso raccomandare le seguenti macro funzioni, 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 in posizione puoi semplicemente dire

NEW_ARRAY(sieve, length);

Per gli array non dinamici, la terza funzione macro deve avere

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

che rende i loop array più sicuri e più convenienti:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}

No ; non si esegue il risultato, poiché:

  • Non è necessario, in quanto in questo caso void * viene promosso automaticamente e in sicurezza a qualsiasi altro tipo di puntatore.
  • Aggiunge clutter al codice, i cast non sono molto facili da leggere (specialmente se il tipo di puntatore è lungo).
  • Ti fa ripetere te stesso, che è generalmente cattivo.
  • Può nascondere un errore se hai dimenticato di includere <stdlib.h> . Ciò può causare arresti anomali (o, peggio, non causare un arresto anomalo fino a un momento successivo in una parte completamente diversa del codice). Considera cosa succede se i puntatori e gli interi sono di dimensioni diverse; quindi stai nascondendo un avviso tramite il cast e potresti perdere parti dell'indirizzo restituito. Nota: a partire da C11 le funzioni implicite sono passate da C, e questo punto non è più rilevante in quanto non esiste un'assunzione automatica che le funzioni non dichiarate restituiscano int .

Come chiarimento, tieni presente che ho detto "non si cast", non "non è necessario il cast". A mio parere, è un fallimento 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.

Si noti inoltre, come sottolineano i commentatori, che quanto sopra parla di diritto C, non di C ++. Credo fermamente in C e C ++ come lingue separate.

Per aggiungere ulteriori informazioni, il codice ripete inutilmente le informazioni sul tipo ( int ) che possono causare errori. È meglio dereferenziare il puntatore utilizzato per memorizzare il valore di ritorno, per "bloccare" i due insieme:

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

Ciò sposta anche la length in avanti per una maggiore visibilità e abbassa le parentesi ridondanti con sizeof ; sono necessari solo quando l'argomento è un nome di tipo. Molte persone sembrano non sapere (o ignorare) questo, il che rende il loro codice più dettagliato. Ricorda: sizeof non è una funzione! :)

Mentre si sposta la length in avanti 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);

Dato che mantenere la dimensione di prima, in questo caso, assicura che la moltiplicazione avvenga con almeno la matematica size_t .

Confronta: malloc(sizeof *sieve * length * width) vs. malloc(length * width * sizeof *sieve) il secondo può overflow della length * width quando width e length sono più piccoli di size_t .


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

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

Vorrei che non esistesse (e non in C ++) e così ho lanciato. Rappresenta il mio gusto e la mia politica di programmazione. Non sto solo lanciando un puntatore, ma effettivamente, lanciando una scheda e scacciando demoni di stupidità . Se non riesco 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 funzioni che restituiscono unsigned char * , e in pratica non utilizzare mai void * nel codice. Se hai bisogno di un puntatore generico a qualsiasi oggetto, usa un char * o un char * unsigned char * e hai cast in entrambe le direzioni. L'unico rilassamento che può essere indulgere, forse, sta usando funzioni come memset e memcpy senza cast.

Per quanto riguarda il cast e la compatibilità con C ++, se scrivi il tuo codice in modo che compili sia come C che come C ++ (nel qual caso devi trasmettere il valore restituito di malloc quando lo assegni a qualcosa di diverso da void * ), puoi farlo a very helpful thing for yourself: you can use macros for casting which translate to C++ style casts when compiling as C++, but reduce to a C cast when compiling as 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 della tua base di codice per questi identificatori ti mostrerà dove sono tutti i tuoi cast, quindi puoi verificare se alcuni di essi sono errati.

Quindi, andando avanti, se si compila regolarmente il codice con C ++, si 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 aiutarvi ad aderire a queste macro, il compilatore GNU C ++ (non C!) Ha una bella funzione: una diagnostica opzionale che viene prodotta per tutte le occorrenze dei cast di stile C.

     -Wold-style-cast (C++ and Objective-C++ only)
         Warn if an old-style (C-style) cast to a non-void type is used
         within a C++ program.  The new-style casts (dynamic_cast,
         static_cast, reinterpret_cast, and const_cast) are less vulnerable
         to unintended effects and much easier to search for.

Se il tuo codice C viene compilato come C ++, puoi usare questa -Wold-style-castopzione per scoprire tutte le occorrenze della (type)sintassi di casting che possono insinuarsi nel codice e seguire questi test diagnostici sostituendolo con un'opportuna scelta tra le macro sopra (o una combinazione, se necessario).

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


In C, non è necessario eseguire il cast del valore di ritorno di malloc . Il puntatore a void restituito da malloc viene convertito automagicamente nel tipo corretto. Tuttavia, se vuoi che il tuo codice venga compilato con un compilatore C ++, è necessario un cast. Un'alternativa preferita tra le comunità è di utilizzare quanto segue:

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

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

I cast sono cattivi, come hanno sottolineato le persone. Puntatori appositamente puntati.


In C è possibile convertire implicitamente un puntatore void in qualsiasi altro tipo di puntatore, quindi un cast non è necessario. Usarne uno può suggerire all'osservatore casuale che ci sono alcune ragioni per cui è necessario, il che può essere fuorviante.


Aggiungendo solo la mia esperienza, studiando ingegneria informatica vedo che i due o tre professori che ho visto scrivere in C scrivono sempre 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 far entrare gli studenti nella mentalità di essere assolutamente specifici. Essenzialmente il cast non cambierà nulla nel modo in cui funziona, fa esattamente quello che dice, alloca la memoria, e il cast non lo fa, ottieni la stessa memoria, e anche se lo lanci in qualcos'altro per errore (e in qualche modo elude il compilatore errori) C accederà allo stesso modo.

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


Preferisco fare il cast, ma non manualmente. Il mio preferito è l'utilizzo g_newe le g_new0macro di glib. Se non si usa glib, aggiungerei macro simili. Queste macro riducono la duplicazione del codice senza compromettere la sicurezza del tipo. Se si ottiene il tipo sbagliato, 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_newe g_new0otterrai un errore. g_newed g_new0entrambi prendono gli stessi argomenti, diversamente da mallocquello che richiede meno argomenti rispetto a calloc. Basta aggiungere 0per ottenere la memoria inizializzata a zero. Il codice può essere compilato con un compilatore C ++ senza modifiche.


Non è obbligatorio trasmettere i risultati di malloc , poiché restituisce void* e un void* può essere indirizzato a qualsiasi tipo di dati.


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

  1. Crea il tuo programma compilato tramite un compilatore C con tutti gli avvisi attivati -Walle correggi tutti gli errori e gli avvertimenti
  2. Assicurarsi che non ci siano variabili dichiarate come auto
  3. Quindi compilarlo usando un compilatore C ++ con -Walle -std=c++11. Correggi tutti gli errori e gli avvertimenti.
  4. Ora compila di nuovo usando il compilatore C. Il tuo programma dovrebbe ora compilare senza alcun avviso e contenere meno bug.

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

malloc non è stato dichiarato all'interno di 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 bersaglio.

Gli unici benefici derivanti dalla scrittura in C anziché in C ++ che posso trovare sono

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

Si noti che i secondi svantaggi dovrebbero nel caso ideale scomparire quando si utilizza il sottoinsieme comune a C insieme con la caratteristica polimorfica statica .

Per coloro che trovano le regole rigide del C ++ inopportune, possiamo usare la funzione C ++ 11 con il tipo dedotto

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

Il concetto dietro il puntatore void è che può essere castato a 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 a fare il debug


Il cast è solo per C ++ non C. Nel caso in cui si usi un compilatore C ++ è meglio cambiarlo in C compilatore.


Dipende dal linguaggio di programmazione e dal compilatore. Se si usa malloc in C, non è necessario digitare cast, dato che verrà automaticamente digitato, Tuttavia se si usa C ++, si dovrebbe digitare cast perché malloc restituirà un tipo void* .


Il tipo restituito è void *, che può essere convertito nel tipo desiderato di puntatore dati per poter essere dereferenziabile.


Un puntatore vuoto è un puntatore generico e C supporta la conversione implicita da un tipo di puntatore vuoto ad altri tipi, quindi non è necessario inserirlo in modo esplicito.

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


Come altri affermato, non è necessario per C, ma per C ++. Se pensi di compilare il tuo codice C con un compilatore C ++, per quale motivo, puoi usare una macro invece, 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 ++.


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

È possibile memorizzare il risultato di malloc in qualsiasi variabile puntatore senza cast, poiché 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 assegnamento o se desideri che il tuo codice venga eseguito nella tradizionale C.

E infatti 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 una matrice di tali oggetti nello spazio allocato (fino al lo spazio è esplicitamente deallocato)


Diversi modi di lanciare da e verso Enum

enum orientation : byte
{
 north = 1,
 south = 2,
 east = 3,
 west = 4
}

class Program
{
  static void Main(string[] args)
  {
    orientation myDirection = orientation.north;
    Console.WriteLine(“myDirection = {0}”, myDirection); //output myDirection =north
    Console.WriteLine((byte)myDirection); //output 1

    string strDir = Convert.ToString(myDirection);
        Console.WriteLine(strDir); //output north

    string myString = “north”; //to convert string to Enum
    myDirection = (orientation)Enum.Parse(typeof(orientation),myString);


 }
}




c malloc casting