Typedef - STB1019/SkullOfSummer GitHub Wiki
Fonte : WG14/N1256 Committee Draft — Septermber 7, 2007 ISO/IEC 9899:TC3 (ISO 31−11, ISO/IEC 646, ISO/IEC 2382−1:1993, ISO 4217, ISO 8601, ISO/IEC 10646, IEC 60559:1989)Link
La keyword typedef
non crea alcun nuovo tipo di variabile, ma ne rinomina uno specifico (creandone un sinonimo) al fine di migliorare la leggibilità del codice e, alla bisogna, rendere portabile l'algoritmo rispetto alla tipologia di dato trattato.
Sintassi
typedef tipo da rinominare identificativo;
Ciascun identificativo è univoco: non è permessa l'omonimia.
Esempio
...
typedef long int intero;
long int a=4294967295;
intero b=1;
printf("%u + %u = %u",a,b,a+b);
...
Di fatto a
e b
sono del medesimo tipo e non differiscono per alcunché se non che, qualsiasi dichiarazione di variabile successiva sarà certamente più comoda e pratica, utilizzanto intero
anziché long int
.
Leggibilità
Il vantaggio implicito di typedef
è palese utilizzando tipi di dato più complessi:
typedef struct anagrafe{
char nome[255],cognome[255];
};
...
struct anagrafe user_1;
...
strcpy(user_1.nome,"Gastaldo");
...
In un secondo momento, posso liberamente modificare la mia struttura, purché definisca quest'ultima con l'identificativo subject
:
typedef struct {
char nome[120],cognome[120];
char indirizzo[255],citta[255],provincia[255],regione[255],nazione[255];
}subject;
...
subject user_1;
...
strcpy(user_1.nome,"Gastaldo");
...
Vantaggio: anziché utilizzare struct anagrafe
per la dichiarazione di nuove variabili, è più conveniente usare subject
avendo dichiarato precedentemente typedef struct {...} subject
.
Compatibilità
In virtù della leggibilità del codice, typedef
è estremamente comodo con tipologie di dato simili (per non dire identiche).
Stessa tipologia
Di seguito dichiariamo due sinonimi per la medesima struttura
typedef struct {int value;} primo;
typedef struct {int value;} secondo;
...
primo a;
secondo b;
...
a.value=5;
b.value=6;
printf("%d",a.value+b.value); //11
La dichiarazione del sinonimo secondo
può essere scritta riutilizzando primo
, e non cambierebbe alcunchè all'interno del programma.
typedef sui tag
typedef struct person_t {
char* name;
char* surname;
} person_t;
typedef union {
int int_val;
char char_val;
long long_val;
char* string_val;
} val_t;
typedef enum {
PERMISSION_READ,
PERMISSION_WRITE,
PERMISSION_EXECUTE
} permissions_t;
Curiosità
Linus Torvalds e typedef
Noi prima abbiamo detto che i typedef sono incredibilmente utili per migliorare la leggibilità del codice. In realtà questo argomento è abbastanza dibattuto: alcuni progetti opensource credono in questa filofosia e li usano abbondantemente. Altri invece credono nel contrario (Linus Torvalds per primo) e ne limitano l'uso in casi ben specifici.
In seguito diamo una trattazione del perché Torvalds, nel kernel, ne fa un uso limitato.
Bisogna per prima cosa capire che typedef
nasconde il vero tipo di una variabile. Per esempio la seguente variabile:
minute_t starting_minute;
di che tipo è? Dal nome sembra facile capire che si tratta di un int che, può va da 0 a 59. Ma se il typedef
è stato utilizzato male si può arrivare a cose del tipo:
actual_model_t model;
di che tipo è la variabile model
? Il manutentore è obbligato a viaggiare nel codice sorgente per capire qual'è il vero tipo della variabile. Questo crea possibili problemi. Per esempio, Torvalds ha esplicitamente sostenuto le sue paure che nascondere il tipo di una variabile importante nasconde la sua struttura: per esempio un programmatore non attento potrebbe passare la variabile model
per valore in tutte le sue chiamate a funzione non sapendo che in realtà tale vaariabile è una gigantesca struttura: questo rallenterebbe enormenente le performance.
Il problema, per Torvalds, è che typedef
nasconda il vero tipo di una variabile: un programmatore vuole sapere se la variabile è primitiva, è una enum
, una struct
o una union
. Per questo preferisce:
void foo(struct model_t m, enum driver_type_t dt, union additional_value_t av);
piuttosto che:
void foo(model_t m, driver_type_t dt, additional_value_t av);
Nella prima si capisce cosa sono le variabili, nella seconda no. Inoltre in questo modo è possibile sfruttare i "tag spaces" degli enum
, union
e struct
.
Citando Linus:
Despite all the previous fuss about the problems of typedefs, i've never had any problem with using typedefs in various code i wrote.
Big things should have big names. That's why "u8" is u8, because it's not just physically small, it also has very little semantics associated with it.
I want those variable declarations to stand out, and make people understand that this is not just a variable, it's a structure, and it may be taking up a noticeable amount of space on the stack, for example.
That's the main issue for me. I don't personally care so much about trying to avoid dependencies in the header files that can also be problematic. That's probably partly because I use fast enough machines that parsing them a few extra times doesn't much bother me, and circular requirements tend to be rare enough not to bother me unduly.
So the thing is a big red warning sign that you're now using a complex data structure, and you should be aware of the semantics that go with it.
Secondo Linus, i typedef
dovrebbe essere utilizzati solo per tipi con contributo semantico basso nel programma, per puntatori a funzioni (notoriamente lunghi da scrivere) o per tipi completamente opachi che non hanno una vera e propria portabilità nel programma e che quindi non ha senso lasciarli accessibili al programmatore.
Funzioni
I typedef sono incredibilmente utili per definire in modo comodo tipi di puntatori a funzione:
void check(char *,char *,int *);
....
typedef void (*g)(char *,char *, int *);
g eq=✓
....
eq("string_1","string_2",res);
check("string_1","string_2",res);
#Referenze