C/Conversioni di tipo

< C
Indice del libro

Il C è un linguaggio debolmente tipizzato, questo significa che una variabile di un tipo può essere vista dal programma come di altro tipo e convertita in un altro tipo (operazione detta casting). In C esistono due tipi di conversioni di tipo:

  1. Conversioni automatiche o implicite
  2. Cast: conversione esplicita

Conversioni implicite

modifica

Quando un operatore ha operandi di tipo diverso, questi vengono convertiti in un tipo comune. In generale, le uniche conversioni automatiche sono quelle che trasformano un operando "più piccolo" in uno "più grande" in modo da non avere perdite di informazione. Per quanto riguarda i tipi base la conversione automatica segue il seguente schema:

charshortintlongfloatdouble

in particolare nelle assegnazioni se il valore a destra dell'operatore di assegnamento è di un tipo di quelli più a sinistra nello schema e il valore a sinistra è di un tipo di quelli più a destra la conversione è automatica.

Un'altra conversione di tipo automatica è da void *, puntatore a tipo vuoto, verso puntatori a qualsiasi altro tipo e da puntatori a qualsiasi tipo a void *.

Quindi assegnazioni di tale tipo sono lecite:

int main (void)
{
    char c = 'A';
    char *cPtr = &c; 
    // cPtr punta a c
    void *vPtr = cPtr;        
    // vPtr punta a c
    cPtr = vPtr;
    // cPtr punta a c
}

Si tratta del metodo definito dal linguaggio per forzare una conversione a un tipo non automatica. La sintassi è la seguente:

(tipo) valore

un esempio ne mostra l'utilità:

#include <stdio.h>
int main (void)
{
    float f;
   
    f = (float)3 / 2;
    printf("%f\n", f); 
}

se non si fosse fatto il cast il risultato di 3/2 avrebbe dato 1 invece di 1,5 in quanto è vero che la conversione al tipo reale (float) è automatica, ma non quella del risultato della divisione tra interi, che dà 1. Basta però che uno solo dei due operandi sia un numero reale che l'intera operazione sia convertita a un numero reale.

In generale la conversione da puntatori a un oggetto a un tipo e viceversa devono essere esplicite. Lo standard afferma che non dovrebbe essere permesso fare un cast di un puntatore a funzione verso un oggetto che non sia un puntatore a funzione e viceversa, e comunque se ciò fosse permesso dal compilatore dipenderebbe dall'implementazione. Inoltre è permesso fare il cast di un intero a un puntatore e viceversa ma il risultato dipende dall'implementazione (non è portabile).

L'unico cast garantito è quello della costante 0 a un puntatore che definisce un puntatore nullo ossia:

void *null = (void *) 0;

è lecito e inoltre il codice precedente può essere semplificato in

void *null = 0;

poiché in tal caso è prevista la conversione implicita.

Un esempio di conversione di puntatori a funzione

modifica

Il cast di un puntatore a funzione potrebbe sembrare un po' innaturale a un principiante quindi se ne mostra la sintassi generale:

(tipo_restituito (*)(elenco tipi argomenti)) nome_puntatore;

gli elementi dell'elenco dei tipi degli argomenti devono essere separati dalla virgola e può contenere anche ... che indica un numero variabil di argomenti. Segue un esempio:

#include <stdio.h>
int fn(char c){                 // in tal caso la dichiarazione 
    return 0;                   // fa anche da prototipo.
}

int main(void){
    void (*fnPtr)(int*) =       /* un puntatore a una funzione che non restituisce nulla 
	                           e ha per argomento un puntatore a intero. */
    	(void (*)(int*))fn;     /* viene inizializzato a fn dopo aver fatto il cast 
                                   al tipo di fnPtr. */                  
		
    if ( fn == (int(*)(char))fnPtr )
	printf ("fnPtr = fn\n");
    else                        // di fatto questo ramo non viene percorso; lo standard 
        printf ("fnPrt diverso" // prevede che tornando al tipo originario l'indirizzo  
                " da fn");      // sia lo stesso.        
}