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
#include <stdio.h>

int main() {
    float numero;
    float parte_frazionaria;

    printf("Inserisci un numero in virgola mobile: ");
    scanf("%f", &numero);

    parte_frazionaria = numero - (int) numero;

    printf("La parte frazionaria di %.2f e' %.2f\n", numero, parte_frazionaria);

    return 0;
}

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:

  1. (int) numero varrà 3. La parte frazionaria viene persa nel cast esplicito ad int;
  2. numero - (int) numero è una sottrazione tra un float ed un int. Per cui (int) numero viene convertito da 3 a 3.0F;
  3. La sottrazione 3.14F - 3.0F è eseguita e il risultato è 0.14F. Questo è il valore della parte frazionaria.

Ricapitolando:

Definizione

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:

  1. Per documentare un'operazione di conversione che potrebbe essere effettuata comunque dal compilatore;

    Ad esempio, se vogliamo convertire un float in un double, 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 a float 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.

  2. 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, sia 3.75 in quanto quoziente è un float. Ma non è così.

    Infatti, il compilatore, per prima cosa, divide dividendo per divisore. Ma, essendo questi ultimi due int, esso effettua la divisione tra int che restituisce un int e tronca il risultato a 3. Solo successivamente, in fase di assegnamento, il risultato viene convertito in float. Ma sarà troppo tardi, in quanto il risultato sarà già troncato e quoziente varrà 3.0.

    In questa situazione ci viene in aiuto il casting. Per ottenere il risultato corretto, 3.75 dobbiamo forzare il compilatore a convertire dividendo o divisore in float prima di effettuare la divisione:

    quoziente = (float) dividendo / divisore;
    

    Da notare che abbiamo effettuato il cast solo su dividendo. Questo è sufficiente in quanto, essendo divisore un int, il compilatore effettuerà la conversione implicita di divisore in float prima di eseguire la divisione.

  3. 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 un long int ma non in un int.

    Tuttavia, la moltiplicazione tra y e se stessa è una moltiplicazione tra int che restituisce un int. Solo successivamente, in fase di assegnamento, il risultato viene convertito in long 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 a long 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.

Definizione

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.