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