Definizione di Tipi con typedef in Linguaggio C
La definizione di nuovi tipi di dato in linguaggio C è una pratica comune che permette di migliorare la leggibilità e la manutenibilità del codice. Utilizzando il costrutto typedef
, i programmatori possono creare alias per tipi di dato esistenti, semplificando la scrittura del codice e rendendolo più intuitivo. Questa tecnica è particolarmente utile in progetti di grandi dimensioni, dove la chiarezza e la coerenza dei tipi di dato possono fare una grande differenza.
In questa lezione, esploreremo come utilizzare typedef
per definire nuovi tipi di dato in C.
Definizione di Tipi
Nelle precedenti lezioni abbiamo visto che il linguaggio C, almeno fino allo standard C99, non metteva a disposizione un tipo propriamente booleano.
Per ovviare a questo problema, nella lezione apposita, abbiamo adoperato come stratagemma l'utilizzo di una macro per definire un tipo booleano:
#define BOOL int
#define TRUE 1
#define FALSE 0
In questo modo, ogniqualvolta avessimo avuto bisogno di un tipo booleano, avremmo potuto dichiarare una variabile in questo modo:
BOOL flag = TRUE;
In effetti, con questo stratagemma abbiamo creato un tipo nuovo, chiamato BOOL
, che è in realtà un int
.
Le macro, come vedremo, funzionano per sostituzione del testo. In altre parole, ogni volta che il preprocessore trova la stringa BOOL
, la sostituisce con int
. Il compilatore, però, non conosce il tipo BOOL
, e quindi non può fare controlli di tipo su di esso.
Esiste, in linguaggio C, un modo più elegante per definire nuovi tipi: il costrutto typedef
.
Proviamo ad usarlo per definire un tipo booleano:
typedef int BOOL;
La prima cosa da notare è che, rispetto alla macro, abbiamo invertito l'ordine di int
e BOOL
. Ossia, il nome del nuovo tipo va inserito alla fine.
Il vantaggio di adoperare typedef
è che con esso creiamo un nuovo tipo riconosciuto dal compilatore. Adesso BOOL
sarà aggiunto alla lista dei tipi gestiti dal compilatore e potrà essere adoperato allo stesso modo.
Possiamo definire variabili di tipo BOOL
:
BOOL flag = TRUE;
Il compilatore tratterà BOOL
come sinonimo di int
. Per cui la variabile flag
sarà nient'altro che un int
.
Ricapitolando:
Definizione di Tipi con typedef
Il costrutto typedef
permette di definire nuovi tipi di dato in linguaggio C. La sintassi è la seguente:
typedef tipo nuovo_tipo;
Dove tipo
è il tipo di dato da cui si vuole creare il nuovo tipo, e nuovo_tipo
è il nome del nuovo tipo.
Vantaggi dell'uso di typedef
Sorge, a questo punto, una domanda: ma se un tipo definito con typedef
non è altro che l'alias di un altro tipo, qual è il vantaggio di adoperare typedef
?
-
La prima risposta è che
typedef
rende i programmi più leggibili, ammesso che la scelta dei nomi dei nuovi tipi sia fatta con cura.Ad esempio, supponiamo di voler definire un tipo per rappresentare una quantità di denaro. Possiamo farlo in questo modo:
typedef float Euro;
Quindi possiamo dichiarare delle variabili di tipo
Euro
:Euro prezzo_articolo, saldo_conto_corrente;
In questo modo, la riga di codice diventa sicuramente più leggibile rispetto a:
float prezzo_articolo, saldo_conto_corrente;
-
La seconda motivazione sta nel fatto che possiamo modificare il tipo di dato sottostante in modo più semplice e sicuro.
Ritornando all'esempio di sopra, se ad un certo punto ci accorgiamo che vogliamo modificare il tipo
Euro
dafloat
adouble
, possiamo farlo in modo molto semplice cambiando una singola riga di codice:typedef double Euro;
La dichiarazione delle variabili
prezzo_articolo
esaldo_conto_corrente
non cambierà, e il compilatore si occuperà di fare le conversioni necessarie.Se non avessimo usato
typedef
, avremmo dovuto cambiare il tipo di tutte le variabili dichiarate comefloat
, con il rischio di dimenticarne qualcuna. -
Infine, il terzo vantaggio riguarda la semplicità di scrittura del codice quando abbiamo a che fare con strutture dati, enumerazioni e puntatori. Vedremo in seguito come
typedef
possa rendere più leggibile il codice in questi casi.
typedef
e Portabilità del codice
Un'ultima considerazione riguarda la portabilità del codice.
Il linguaggio C è stato progettato per essere portabile. Questo significa che un programma scritto in C dovrebbe poter essere compilato su qualsiasi sistema, indipendentemente dall'architettura hardware o dal sistema operativo.
Tuttavia, non si ha la garanzia che i tipi di dato standard abbiano la stessa dimensione su tutti i sistemi. Ad esempio, il tipo int
potrebbe essere a 16 bit su un sistema e a 32 bit su un altro.
Ad esempio:
int i = 100000;
Se int
è a 16 bit, il valore di i
sarà troncato a 16 bit, e il risultato sarà diverso rispetto a quando int
è a 32 bit. Ossia, il codice di sopra funziona su una macchina a 32 bit, ma non su una a 16 bit.
Allora, in questi casi possiamo adoperare typedef
per definire tipi interi e modificare la dimensione di essi a seconda delle necessità.
Per cui, tornando all'esempio di sopra, possiamo definire un tipo INTERO
che è un int
su di una macchina a 32 bit e un long
su di una a 16 bit:
/* Macchina a 32 bit */
typedef int INTERO;
/* Macchina a 16 bit */
typedef long INTERO;
Facendo così, la dichiarazione:
INTERO i = 100000;
Funzionerà su entrambe le macchine, perché INTERO
sarà un int
su una macchina e un long
sull'altra.
Questa tecnica non risolve tutti i nostri problemi di portabilità anche perché molti dipendono dal modo in cui le variabili vengono adoperate.
Ad esempio, il problema si pone con l'utilizzo delle funzioni scanf
e printf
, che dipendono dal tipo di dato passato come argomento. In questi casi, è necessario fare attenzione a come si scrive il codice per garantire la portabilità. Le due funzioni richiedono uno specificatore di formato differente a seconda del tipo di dato passato: %d
per int
e %ld
per long
.
La stessa libreria standard del C, tuttavia, adopera typedef
per garantire la portabilità del codice. Ad esempio, il tipo size_t
è definito come unsigned int
su alcune macchine e unsigned long
su altre.
In generale, l'uso di typedef
può aiutare a rendere il codice più portabile, ma non risolve tutti i problemi di portabilità.
In Sintesi
In questa lezione abbiamo visto come definire nuovi tipi di dato in linguaggio C utilizzando il costrutto typedef
:
- Abbiamo visto come definire un tipo booleano con
typedef
e le differenze rispetto all'uso di macro. - Abbiamo discusso i vantaggi dell'uso di
typedef
per rendere il codice più leggibile e per facilitare la modifica dei tipi di dato. - Abbiamo parlato della portabilità del codice e di come
typedef
possa aiutare a rendere il codice più portabile.
In generale, l'uso di typedef
è una pratica comune in linguaggio C per definire nuovi tipi di dato e rendere il codice più leggibile e manutenibile.