Casting di Tipi in Linguaggio C
Il casting di tipi in linguaggio C è una tecnica fondamentale che consente di forzare la conversione di una variabile da un tipo di dato a un altro. Questa operazione è particolarmente utile quando si desidera avere un controllo preciso sulle conversioni dei tipi, evitando comportamenti indesiderati che potrebbero verificarsi con le conversioni implicite. Il casting esplicito permette al programmatore di indicare chiaramente al compilatore come trattare i dati, migliorando la leggibilità e la manutenzione del codice.
In questa lezione, esploreremo in dettaglio come funziona il casting di tipi in C, quando è utile utilizzarlo e quali sono le sue applicazioni pratiche
Conversione Esplicita dei Tipi: Casting
Nella lezione precedente abbiamo visto come il linguaggio C gestisca la conversione implicita dei tipi aritmetici. Ne abbiamo studiato le regole e visto quando esse si applicano.
Spesso, però, abbiamo bisogno di un maggior controllo sulla conversione dei tipi. Per questi casi, il linguaggio C mette a disposizione la conversione esplicita dei tipi che viene più comunemente chiamata casting.
Per effettuare un cast esplicito di un tipo ad un altro, si utilizza la seguente sintassi:
(tipo) espressione;
Dove, tipo
è il tipo di dato a cui vogliamo convertire l'espressione e espressione
è l'espressione che vogliamo convertire.
Prendiamo un esempio. Supponiamo di voler calcolare la parte frazionaria di un numero in virgola mobile. Possiamo farlo in modo molto semplice effettuando un casting esplicito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Abbiamo applicato il casting alla riga 10 dell'esempio:
parte_frazionaria = numero - (int) numero;
In questo caso, abbiamo dapprima convertito numero
in intero attraverso la sotto-espressione:
(int) numero
In questo modo, abbiamo forzato la conversione di numero
ad un intero perdendo, così, la parte frazionaria.
La successiva operazione comprende la sottrazione tra numero
, che è un float
, e (int) numero
che è un intero. In base alle regole di conversione implicita che abbiamo visto nella lezione precedente, prima di effettuare la sottrazione, il compilatore converte l'intero (int) numero
in un float
e poi esegue la sottrazione.
Per chiarezza, vediamo con un esempio numerico. Supponiamo di aver inserito il numero 3.14
. La variabile numero
varrà 3.14F
. Per cui:
(int) numero
varrà3
. La parte frazionaria viene persa nel cast esplicito adint
;numero - (int) numero
è una sottrazione tra unfloat
ed unint
. Per cui(int) numero
viene convertito da3
a3.0F
;- La sottrazione
3.14F - 3.0F
è eseguita e il risultato è0.14F
. Questo è il valore della parte frazionaria.
Ricapitolando:
Casting di Tipi in Linguaggio C
Il Casting di Tipi, o Conversione Esplicita dei Tipi, è un'operazione che permette di forzare la conversione di un tipo di dato in un altro.
La sintassi per convertire un tipo di dato in un altro è la seguente:
(tipo) espressione;
Dove tipo
è il tipo di dato a cui vogliamo convertire l'espressione e espressione
è l'espressione che vogliamo convertire.
Utilizzi del Casting
In linguaggio C, il casting viene sostanzialmente adoperato per due scopi:
-
Per documentare un'operazione di conversione che potrebbe essere effettuata comunque dal compilatore;
Ad esempio, se vogliamo convertire un
float
in undouble
, possiamo scrivere:float f; /* ... */ double d = (double) f;
Anche se il compilatore avrebbe effettuato la conversione implicita, il casting esplicito documenta l'intenzione del programmatore.
In altri casi, il compilatore evita di mostrare un warning. Ad esempio:
double d; /* ... */ float f = (float) d;
In questo caso la conversione da
double
afloat
potrebbe causare una perdita di precisione. In assenza del casting esplicito, il compilatore avrebbe comunque effettuato la conversione, ma avrebbe mostrato un warning.Attraverso il casting, stiamo sostanzialmente dicendo al compilatore che si tratta di un comportamento voluto e non una svista.
-
Per forzare una conversione che il compilatore non effettuerebbe;
Prendiamo un semplice esempio:
float quoziente; int dividendo = 15; int divisore = 4; quoziente = dividendo / divisore;
Leggendo questo codice, a prima vista si potrebbe pensare che il risultato finale, ossia il valore della variabile
quoziente
, sia3.75
in quantoquoziente
è unfloat
. Ma non è così.Infatti, il compilatore, per prima cosa, divide
dividendo
perdivisore
. Ma, essendo questi ultimi dueint
, esso effettua la divisione traint
che restituisce unint
e tronca il risultato a3
. Solo successivamente, in fase di assegnamento, il risultato viene convertito infloat
. Ma sarà troppo tardi, in quanto il risultato sarà già troncato equoziente
varrà3.0
.In questa situazione ci viene in aiuto il casting. Per ottenere il risultato corretto,
3.75
dobbiamo forzare il compilatore a convertiredividendo
odivisore
infloat
prima di effettuare la divisione:quoziente = (float) dividendo / divisore;
Da notare che abbiamo effettuato il cast solo su
dividendo
. Questo è sufficiente in quanto, essendodivisore
unint
, il compilatore effettuerà la conversione implicita didivisore
infloat
prima di eseguire la divisione. -
Evitare casi di overflow.
In alcuni casi, il casting esplicito risulta necessario per evitare condizioni di overflow.
Prendiamo un esempio:
long int x; int y = 1000000; x = y * y;
A prima vista, potremmo pensare che il codice di sopra sia corretto. Ma non è così. Il risultato della moltiplicazione
y * y
è1000000000000
che può essere memorizzato in unlong int
ma non in unint
.Tuttavia, la moltiplicazione tra
y
e se stessa è una moltiplicazione traint
che restituisce unint
. Solo successivamente, in fase di assegnamento, il risultato viene convertito inlong int
. Ma sarà troppo tardi, in quanto il risultato sarà già andato in overflow restituendo un risultato errato:-727379968
.Possiamo evitare questo problema adoperando il casting esplicito su uno dei due operandi:
x = ((long) y) * y;
In questo modo, il primo operando della moltiplicazione viene convertito in
long
prima di effettuare la moltiplicazione. Non è necessario convertire esplicitamente il secondo operando in quanto il compilatore effettuerà la conversione implicita along
prima di eseguire la moltiplicazione.Il risultato sarà corretto e
x
varrà1000000000
.
Casting e Regole di Precedenza
In precedenza abbiamo studiato le regole che determinano la precedenza di valutazione degli operatori nelle espressioni.
In tale schema generale, qual è la precedenza del casting rispetto agli altri operatori?
Il linguaggio C considera l'operazione di casting, (tipo)
, come un operatore unario. Questo significa che il casting ha la stessa precedenza degli altri operatori unari, e quindi ha una precedenza maggiore rispetto agli operatori binari.
Per cui, ritornando all'esempio della divisione abbiamo che l'espressione:
quoziente = (float) dividendo / divisore;
è valutata come:
quoziente = ((float) dividendo) / divisore;
Lo stesso risultato lo avremmo potuto ottenere con la seguente espressione:
quoziente = dividendo / (float) divisore;
Oppure, in modo più esplicito:
quoziente = (float) dividendo / (float) divisore;
In tutti i casi, il risultato è lo stesso. Il casting viene effettuato prima della divisione.
Ovviamente, nei casi in cui non si è sicuri della precedenza oppure per rendere il codice più leggibile, è sempre possibile utilizzare le parentesi per forzare l'ordine di valutazione.
Casting e Regole di Precedenza
Il Casting è considerato un operatore unario e ha la stessa precedenza degli altri operatori unari. Per cui, ha una precedenza maggiore rispetto agli operatori binari.
Per forzare l'ordine di valutazione, è possibile utilizzare le parentesi.
In Sintesi
Il casting di tipi è un'operazione che permette di forzare la conversione di un tipo di dato in un altro. Questa operazione è molto utile per controllare la conversione dei tipi e per evitare comportamenti indesiderati.
Abbiamo visto in questa lezione che:
- Il casting di tipi si effettua attraverso la sintassi
(tipo) espressione;
; - Il casting è un operatore unario e ha la stessa precedenza degli altri operatori unari;
- Il casting viene utilizzato per documentare un'operazione di conversione, per forzare una conversione che il compilatore non effettuerebbe e per evitare casi di overflow.