- Programmazione » Programmazione » Guida C - Manuale programmazione con articoli e risorse interessanti
Gli operatori: cast e conversioni di tipo
Cast e conversioni di tipo
In una espressione è sempre possibile avere operandidi tipo diverso. Non è poi così stranodividere, ad esempio, un numero in virgola mobile per unnumero intero, oppure, anche se a prima vista puòsembrare meno ovvio, moltiplicare un intero per un carattere.In ogni caso, comunque, il risultato dell'operazione deveessere di un unico tipo, di volta in volta ben determinato:in tali casi è sempre necessario, perciò,procedere a conversioni di tipo su almeno uno degli operandicoinvolti.
Il C, al riguardo, fissa un ordine "gerarchico"dei tipi di dato intrinseci, e stabilisce due semplici regoleche consentono di conoscere sempre a priori come verrannoeffettuate le necessarie conversioni.
L'ordine gerachico dei tipi, decrescente da sinistra adestra, è il seguente:
1.
<tt>
long
double
>
double
>
float
>
long
>
int
>
short
>
char
</tt>
Ne risulta che ogni tipo è di "grado"superiore ad ogni altro tipo elencato alla sua destra e digrado inferiore a quello dei tipi elencati alla sua sinistra.Sulla scorta di tale gerarchia, la prima regola stabilisceche nelle espressioni che non coinvolgono operatori diassegnamento, in ogni coppia di operandi l'operando digrado inferiore è convertito nel tipodell'operando avente grado superiore. Così, adesempio, in una operazione di confronto tra un floate un long, quest'ultimo è convertito infloat prima che sia effettuato ilconfronto.
La seconda regola riguarda invece le operazioni diassegnamento: l'espressione a destra dell'operatoredi assegnamento è sempre convertita nel tipo dellavariabile che si trova a sinistra del medesimo,indipendentemente dal livello gerarchico dei daticoinvolti.
Naturalmente le due regole possono trovare contemporaneaapplicazione quando ad una variabile sia assegnato ilrisultato di un'espressione che coinvolge operandi ditipi differenti:
1.
<tt>
int
iVar;
long
lVar;
float
fVar;
char
cVar; .... iVar = fVar + lVar * cVar;</tt>
Nell'esempio, l'operatore di moltiplicazione haprecedenza rispetto a quello di somma, perciò vienedapprima calcolato il prodotto di lVar percVar, dopo avere convertito cVar inlong. Il valore ottenuto è poi sommato aquello contenuto in fVar, ma solo dopo averloconvertito in float. Il risultato, infine, vieneconvertito in int ed assegnato aiVar.
Si tenga presente che le conversioni effettuate in modoautomatico dal compilatore C implicano un troncamento dellaparte più significativa del valore convertito quandoesso viene "degradato" ad un livello inferiore, edun'aggiunta di bit nulli quando è"promosso" ad un tipo di livello superiore. Nelsecondo caso il valore originario del dato può semprevenire conservato; nel primo, al contrario, esiste il rischiodi perdere una parte (la più significativa) del valoreconvertito.
L'affermazione risulta palese se si pensa, ad esempio,al caso di una conversione da int a long eduna viceversa: consideriamo due variabili, la prima di tipoint (16 bit) e la seconda di tipo long (32 bit),contenenti, rispettivamente, i valori 5027 (che incodice binario è 0001001110100011) e2573945 (in binario00000000001001110100011001111001): la conversionedella prima in long implica l'aggiunta di 16 bitnulli alla sinistra di quelli "originali". Lospazio occupato è ora di 32 bit, ma il valore dipartenza non viene modificato. Nel convertire illong in int, al contrario, vengonoeliminati i 16 bit più significativi (quellipiù a sinistra): i 16 bit rimanenti sono0100011001111001, che equivalgono, in notazionedecimale, a 18041.
Conversioni di tipo automatiche sono effettuate anche quandoil tipo dei parametri passati ad una funzione non corrispondeal tipo dei parametri che la funzione "desidera".Inoltre, in questo caso, i char sono sempreconvertiti in int, anche se la funzione si aspettadi ricevere proprio un char[3]. Va anche sottolineato che ilcompilatore, in genere, emette un messaggio di warning quandola conversione di tipo generata in modo automatico comportail rischio di perdere una parte del valorecoinvolto.
Vi sono però spesso situazioni in cui il compilatorenon è in grado di effettuare la conversione in modoautomatico; ad esempio quando sono coinvolti tipi di dato nonintrinseci, definiti dal programmatore (quali strutture,campi di bit, etc.). Altre volte, invece, si desiderasemplicemente esplicitare una conversione che il compilatorepotrebbe risolvere da sé, al fine di renderepiù chiaro il codice o per evitare il warning ad essacorrelato.
In tutti questi casi si può ricorrereall'operatore di cast, il quale forza un qualunquevalore ad appartenere ad un certo tipo. La notazione èla seguente:
1.
<tt>(tipo)espressione</tt>
dove tipo può essere una qualsiasi delleparole chiave del C utilizzate nelle dichiarazioni di tipo edespressione dev'essere una qualsiasi espressionesintatticamente corretta. Ad esempio:
1.
<tt>
int
iVar; iVar = (
int
)
3.14159
;</tt>
La conversione illustrata può essere automaticamenteeseguita dal compilatore, ma l'esplicitarla mediantel'operatore di cast incrementa la chiarezza del codice edevita il messaggio di warning. Un altro caso in cui sieffettua spesso il cast è l'inizializzazione di unpuntatore far o huge con una costante a 32bit:
1.
<tt>
char
far *colVbuf = (
char
far *)0xB8000000L;
// ptr buffer video testo col.</tt>
La conversione automatica, in questo caso, non comporterebbealcun errore, dal momento che la costante assegnata alpuntatore è un dato a 32 bit, esattamente come ilpuntatore stesso: il compilatore emetterebbe però unasegnalazione di warning, per evidenziare al programmatore cheun dato di tipo long viene assegnato ad un puntatorefar a carattere: una questione di forma, insomma. Difatto la costante potrebbe essere scritta anche senza la"L" che ne indica inequivocabilmente lanatura long, ma in quel caso il compilatoresegnalerebbe, con un altro warning, che vi è unacostante che, per il valore espresso, deve essere consideratalong senza che ciò sia stato esplicitamenterichiesto.
Più significativo può essere l'esempioseguente:
1.
<tt>struct FARPTR { unsigned offset; unsigned segment;}; ....
char
far *cFptr; struct FARPTR fPtr; .... (
char
far *)fPtr = cFptr;</tt>
In questo caso la struttura di tipo FARPTR èutilizzata per accedere separatamente alla parte segmento ealla parte offset di un puntatore far. In pratica,il valore contenuto nel puntatore far ècopiato nell'area di memoria occupata dalla struttura: sitratta di un'operazione che potrebbe provocarel'emissione di un messaggio di errore el'interruzione della compilazione. La presenzadell'operatore di cast tranquillizza il compilatore; dalcanto nostro sappiamo che struttura e puntatore occupanoentrambi 32 bit, perciò siamo tranquilli a nostravolta.
- Articolo precedente Gli operatori: autoincremento e autodecremento
- Articolo successivo Gli operatori: operatore sizeof()