Che cosa è esattamente un cast di tipo in C / C ++?

Che cosa è esattamente un cast di tipo in C / C ++? Come verifica il compilatore se è necessario un typecast esplicito (e valido)? Confronta lo spazio richiesto per un valore? Se ho per esempio:

int a; double b = 15.0; a = (int) b; 

Se ricordo bene, un doppio valore richiede più spazio (era 8 byte ?!) di un numero intero (4 byte). E la rappresentazione interna di entrambi è completamente diversa (complemento su due / mantissa). Quindi cosa succede internamente? L’esempio qui è abbastanza semplice, ma in C / C ++ ci sono molte tipizzazioni.

Come fa il compilatore a sapere (o il programmatore) se posso lanciare ad esempio FOO su BAR?

Un cast di tipo è fondamentalmente una conversione da un tipo all’altro. Può essere implicito (cioè, eseguito automaticamente dal compilatore, forse perdendo informazioni nel processo) o esplicito (cioè specificato dallo sviluppatore nel codice). Lo spazio occupato dai tipi è di secondaria importanza. Più importante è l’applicabilità (e talvolta la convocazione) della conversione.

È ansible che le conversioni implicite perdano informazioni, che i segni possano essere persi / acquisiti e che si verifichino overflow / underflow. Il compilatore non ti proteggerà da questi eventi, tranne forse attraverso un avviso che viene generato in fase di compilazione. La segmentazione può verificarsi anche quando un tipo derivato viene convertito implicitamente in un tipo di base (in base al valore).

Per le conversioni che possono essere decisamente pericolose (ad esempio, da una base a un tipo derivato), lo standard C ++ richiede un cast esplicito. Non solo, ma offre cast espliciti più restrittivi, come static_cast , dynamic_cast , reinterpret_cast e const_cast , ognuno dei quali limita ulteriormente il cast esplicito a solo un sottoinsieme di possibili conversioni, riducendo il potenziale di errori di cast.

Le conversioni valide, sia implicite che esplicite, sono in definitiva definite dagli standard C / C ++, sebbene in C ++ lo sviluppatore abbia la possibilità di estendere le conversioni per tipi definiti dall’utente, sia impliciti che espliciti, tramite l’uso di costruttori e operatori sovraccaricati (cast) .

Le regole complete per le quali i cast sono permessi dalle norme e che non lo sono possono diventare abbastanza complicate. Ho cercato di presentare fedelmente un riassunto un po ‘conciso di alcune di quelle regole in questa risposta. Se sei veramente interessato a ciò che è e non è permesso, ti invito vivamente a visitare gli standard e leggere le rispettive sezioni sulla conversione del tipo.

Voglio solo menzionare qualcosa spesso trascurato:

  • Un cast crea sempre un temporaneo del tipo di destinazione (anche se il tipo di destinazione è un riferimento, non si noterà).

Questo può essere importante. Per esempio:

 #include  void change_one_print_other( int& a, const int& b ) { a = 0; std::cout < < b << "\n"; } int main(void) { int x = 5, y = 5; change_one_print_other(x, x); change_one_print_other(y, static_cast(y)); } 

Quel cast sembra inutile. Ma l’ aspetto può essere ingannevole .

Ci sono alcuni tipi di cast che il compilatore sa come fare implicitamente – il doppio di int è uno di questi. Abbassa semplicemente la parte decimale. La rappresentazione interna viene convertita come parte del processo in modo che l’assegnazione funzioni correttamente.

Si noti che ci sono valori troppo grandi per essere convertiti correttamente. Non ricordo quali siano le regole per quel caso; può essere lasciato a discrezione del compilatore.

Crea un piccolo programma C del tuo codice e segui le istruzioni in Come ottenere GCC per generare il codice assembly per vedere come il compilatore esegue un cast di tipi.