|
Appunti informatica |
|
Visite: 1567 | Gradito: | [ Picolo appunti ] |
Leggi anche appunti:L'i/o e la gestione dei fileL'I/O e la gestione dei file Per Input/Output (I/O) si intende l'insieme delle Impiego del costrutto monitorImpiego del costrutto monitor X) Si implementi il problema dei L'accessibilitÀ e la durata delle variabiliL'accessibilità e la durata delle variabili In C le variabili possono essere |
In una espressione è sempre possibile avere operandi di tipo diverso. Non è poi così strano dividere, ad esempio, un numero in virgola mobile per un numero 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 deve essere 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 operandi coinvolti.
Il C, al riguardo, fissa un ordine 'gerarchico' dei tipi di dato intrinseci, e stabilisce due semplici regole che consentono di conoscere sempre a priori come verranno effettuate le necessarie conversioni.
L'ordine gerachico dei tipi, decrescente da sinistra a destra, è il seguente:
long double > double > float > long > int > short > char
Ne risulta che ogni tipo è di 'grado' superiore ad ogni altro tipo elencato alla sua destra e di grado inferiore a quello dei tipi elencati alla sua sinistra. Sulla scorta di tale gerarchia, la prima regola stabilisce che nelle espressioni che non coinvolgono operatori di assegnamento, in ogni coppia di operandi l'operando di grado inferiore è convertito nel tipo dell'operando avente grado superiore. Così, ad esempio, in una operazione di confronto tra un float e un long, quest'ultimo è convertito in float prima che sia effettuato il confronto.
La seconda regola riguarda invece le operazioni di assegnamento: l'espressione a destra dell'operatore di assegnamento è sempre convertita nel tipo della variabile che si trova a sinistra del medesimo, indipendentemente dal livello gerarchico dei dati coinvolti.
Naturalmente le due regole possono trovare contemporanea applicazione quando ad una variabile sia assegnato il risultato di un'espressione che coinvolge operandi di tipi differenti:
int iVar;
long lVar;
float fVar;
char cVar;
.
iVar = fVar + lVar * cVar;
Nell'esempio, l'operatore di moltiplicazione ha precedenza rispetto a quello di somma, perciò viene dapprima calcolato il prodotto di lVar per cVar, dopo avere convertito cVar in long. Il valore ottenuto è poi sommato a quello contenuto in fVar, ma solo dopo averlo convertito in float. Il risultato, infine, viene convertito in int ed assegnato a iVar
Si tenga presente che le conversioni effettuate in modo automatico dal compilatore C implicano un troncamento della parte più significativa del valore convertito quando esso viene 'degradato' ad un livello inferiore, ed un'aggiunta di bit nulli quando è 'promosso' ad un tipo di livello superiore. Nel secondo caso il valore originario del dato può sempre venire conservato; nel primo, al contrario, esiste il rischio di perdere una parte (la più significativa) del valore convertito.
L'affermazione risulta palese se si pensa, ad esempio, al caso di una conversione da int a long ed una viceversa: consideriamo due variabili, la prima di tipo int (16 bit) e la seconda di tipo long (32 bit), contenenti, rispettivamente, i valori (che in codice binario è ) e (in binario ): la conversione della prima in long implica l'aggiunta di 16 bit nulli alla sinistra di quelli 'originali'. Lo spazio occupato è ora di 32 bit, ma il valore di partenza non viene modificato. Nel convertire il long in int, al contrario, vengono eliminati i 16 bit più significativi (quelli più a sinistra): i 16 bit rimanenti sono , che equivalgono, in notazione decimale, a
Conversioni di tipo automatiche sono effettuate anche quando il tipo dei parametri passati ad una funzione non corrisponde al tipo dei parametri che la funzione 'desidera'. Inoltre, in questo caso, i char sono sempre convertiti in int, anche se la funzione si aspetta di ricevere proprio un char . Va anche sottolineato che il compilatore, in genere, emette un messaggio di warning quando la conversione di tipo generata in modo automatico comporta il rischio di perdere una parte del valore coinvolto.
Vi sono però spesso situazioni in cui il compilatore non è in grado di effettuare la conversione in modo automatico; ad esempio quando sono coinvolti tipi di dato non intrinseci, definiti dal programmatore (quali strutture, campi di bit, etc.). Altre volte, invece, si desidera semplicemente esplicitare una conversione che il compilatore potrebbe risolvere da sé, al fine di rendere più chiaro il codice o per evitare il warning ad essa correlato.
In tutti questi casi si può ricorrere all'operatore di cast, il quale forza un qualunque valore ad appartenere ad un certo tipo. La notazione è la seguente:
(tipo)espressione
dove tipo può essere una qualsiasi delle parole chiave del C utilizzate nelle dichiarazioni di tipo ed espressione dev'essere una qualsiasi espressione sintatticamente corretta. Ad esempio:
int iVar;
iVar = (int)3.14159;
La conversione illustrata può essere automaticamente eseguita dal compilatore, ma l'esplicitarla mediante l'operatore di cast incrementa la chiarezza del codice ed evita il messaggio di warning. Un altro caso in cui si effettua spesso il cast è l'inizializzazione di un puntatore far o huge con una costante a 32 bit:
char far *colVbuf = (char far *)0xB8000000L; // ptr buffer video testo col.
La conversione automatica, in questo caso, non comporterebbe alcun errore, dal momento che la costante assegnata al puntatore è un dato a 32 bit, esattamente come il puntatore stesso: il compilatore emetterebbe però una segnalazione di warning, per evidenziare al programmatore che un dato di tipo long viene assegnato ad un puntatore far a carattere: una questione di forma, insomma. Di fatto la costante potrebbe essere scritta anche senza la 'L' che ne indica inequivocabilmente la natura long, ma in quel caso il compilatore segnalerebbe, con un altro warning, che vi è una costante che, per il valore espresso, deve essere considerata long senza che ciò sia stato esplicitamente richiesto.
Più significativo può essere l'esempio seguente:
struct FARPTR ;
.
char far *cFptr;
struct FARPTR fPtr;
.
(char far *)fPtr = cFptr;
In questo caso la struttura di tipo FARPTR è utilizzata per accedere separatamente alla parte segmento e alla parte offset di un puntatore far. In pratica, il valore contenuto nel puntatore far è copiato nell'area di memoria occupata dalla struttura: si tratta di un'operazione che potrebbe provocare l'emissione di un messaggio di errore e l'interruzione della compilazione. La presenza dell'operatore di cast tranquillizza il compilatore; dal canto nostro sappiamo che struttura e puntatore occupano entrambi 32 bit, perciò siamo tranquilli a nostra volta.
Qualcuno, probabilmente, se ne domanda il perché. Ebbene, il motivo non è legato ad una improbabile mania del compilatore di convertire tutto quello che gli capiti a tiro, bensì esclusivamente alla natura tecnica del passaggio di parametri ad una funzione, sempre effettuato tramite una particolare area di memoria, lo stack, organizzata e gestita in word (vedere pag.
Appunti su: |
|