Ricerca…


introduzione

Il meccanismo typedef consente la creazione di alias per altri tipi. Non crea nuovi tipi. Le persone usano spesso typedef per migliorare la portabilità del codice, per fornire alias per i tipi di struttura o unione, o per creare alias per i tipi di funzione (o puntatore di funzione).

Nello standard C, typedef è classificato come "classe di memoria" per comodità; si verifica sintatticamente dove potrebbero apparire classi di memoria come static o extern .

Sintassi

  • typedef nome_esistente nome_alias;

Osservazioni


Svantaggi di Typedef

typedef potrebbe portare all'inquinamento del namespace nei grandi programmi in C.

Svantaggi di Typedef Structs

Inoltre, le strutture typedef senza un nome di tag sono una delle cause principali di imposizione inutile delle relazioni di ordinamento tra i file di intestazione.

Tenere conto:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
    struct bar *bar;
};

#endif

Con tale definizione, non usando typedefs , è possibile che un'unità di compilazione includa foo.h per ottenere la definizione FOO_DEF . Se non tenta di dereferenziare il membro della bar della struttura foo non sarà necessario includere il file bar.h

Typedef vs #define

#define è una direttiva per il pre-processore C che viene anche utilizzata per definire gli alias per vari tipi di dati simili a typedef ma con le seguenti differenze:

  • typedef è limitato a dare nomi simbolici a tipi solo dove come #define può essere usato per definire alias anche per valori.

  • typedef interpretazione typedef viene eseguita dal compilatore mentre le istruzioni #define vengono elaborate dal pre-processore.

  • Si noti che #define cptr char * seguito da cptr a, b; non fa lo stesso di typedef char *cptr; seguito da cptr a, b; . Con il #define , b è un semplice char variabile, ma è anche un puntatore con il typedef .

Typedef per strutture e unioni

Puoi dare nomi alias a una struct :

typedef struct Person {
    char name[32];
    int age;
} Person;

Person person;

Rispetto al modo tradizionale di dichiarare le strutture, i programmatori non avrebbero bisogno di avere struct ogni volta che dichiarano un'istanza di quella struttura.

Si noti che il nome Person (al contrario di struct Person ) non è definito fino al punto e virgola finale. Pertanto, per gli elenchi collegati e le strutture ad albero che devono contenere un puntatore allo stesso tipo di struttura, è necessario utilizzare:

typedef struct Person {
    char name[32];
    int age;
    struct Person *next;
} Person;

o:

typedef struct Person Person;

struct Person {
    char name[32];
    int age;
    Person *next;
};

L'uso di un typedef per un tipo di union è molto simile.

typedef union Float Float;

union Float
{
    float f;
    char  b[sizeof(float)];
};

Una struttura simile a questa può essere utilizzata per analizzare i byte che costituiscono un valore float .

Usi semplici di Typedef

Per dare nomi brevi a un tipo di dati

Invece di:

long long int foo;
struct mystructure object;

si può usare

/* write once */
typedef long long ll;
typedef struct mystructure mystruct;

/* use whenever needed */
ll foo;
mystruct object;

Ciò riduce la quantità di digitazione necessaria se il tipo viene utilizzato più volte nel programma.

Migliorare la portabilità

Gli attributi dei tipi di dati variano tra diverse architetture. Ad esempio, un int può essere un tipo a 2 byte in un'implementazione e un tipo a 4 byte in un altro. Supponiamo che un programma debba utilizzare un tipo di 4 byte per funzionare correttamente.

In un'implementazione, la dimensione di int sia 2 byte e quella di long 4 byte. In un altro, la dimensione di int sia 4 byte e quella di long 8 byte. Se il programma è scritto usando la seconda implementazione,

/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */

Affinché il programma venga eseguito nella prima implementazione, tutte le dichiarazioni int dovranno essere modificate a long .

/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */

Per evitare ciò, si può usare typedef

/* program expecting a 4 byte integer */
typedef int myint; /* need to declare once - only one line to modify if needed */
myint foo; /* need to hold 4 bytes to work */
/* some code involving many more myints */

Quindi, solo l'istruzione typedef dovrebbe essere cambiata ogni volta, invece di esaminare l'intero programma.

C99

L'intestazione <stdint.h> e l'intestazione <inttypes.h> correlata definiscono nomi di tipi standard (usando typedef ) per numeri interi di varie dimensioni, e questi nomi sono spesso la scelta migliore nel codice moderno che ha bisogno di numeri interi fissi. Ad esempio, uint8_t è un tipo intero a 8 bit senza segno; int64_t è un tipo intero a 64 bit con int64_t . Il tipo uintptr_t è un tipo intero senza segno abbastanza grande da contenere qualsiasi puntatore all'oggetto. Questi tipi sono teoricamente facoltativi, ma è raro che non siano disponibili. Ci sono varianti come uint_least16_t (il più piccolo tipo di intero senza segno con almeno 16 bit) e int_fast32_t (il tipo di intero con int_fast32_t più veloce con almeno 32 bit). Inoltre, intmax_t e uintmax_t sono i tipi interi più grandi supportati dall'implementazione. Questi tipi sono obbligatori.

Per specificare un utilizzo o migliorare la leggibilità

Se un insieme di dati ha uno scopo particolare, si può usare typedef per dargli un nome significativo. Inoltre, se la proprietà dei dati cambia in modo tale che il tipo di base deve essere modificato, solo l'istruzione typedef deve essere modificata, invece di esaminare l'intero programma.

Typedef per puntatori di funzioni

Possiamo usare typedef per semplificare l'uso dei puntatori di funzione. Immagina di avere alcune funzioni, tutte con la stessa firma, che usano il loro argomento per stampare qualcosa in modi diversi:

#include<stdio.h>

void print_to_n(int n)
{
    for (int i = 1; i <= n; ++i)
        printf("%d\n", i);
}

void print_n(int n)
{
    printf("%d\n, n);
}

Ora possiamo usare un typedef per creare un tipo di puntatore a funzione denominata chiamato stampante:

typedef void (*printer_t)(int);

Questo crea un tipo, denominato printer_t per un puntatore a una funzione che accetta un singolo argomento int e non restituisce nulla, che corrisponde alla firma delle funzioni che abbiamo sopra. Per usarlo creiamo una variabile del tipo creato e assegniamo un puntatore a una delle funzioni in questione:

printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type

Quindi chiamare la funzione puntata dalla variabile del puntatore della funzione:

p(5);           // Prints 1 2 3 4 5 on separate lines
(*p)(5);        // So does this

Pertanto, typedef consente una sintassi più semplice quando si gestiscono i puntatori di funzione. Ciò diventa più evidente quando i puntatori di funzione vengono utilizzati in situazioni più complesse, come gli argomenti per le funzioni.

Se si utilizza una funzione che accetta un puntatore di funzione come parametro senza un tipo di puntatore di funzione definito, la definizione della funzione sarebbe,

void foo (void (*printer)(int), int y){
    //code
    printer(y);
    //code
}

Tuttavia, con typedef è:

void foo (printer_t printer, int y){
    //code
    printer(y);
    //code
}

Allo stesso modo, le funzioni possono restituire i puntatori di funzione e, di nuovo, l'uso di un typedef può semplificare la sintassi quando lo si fa.

Un classico esempio è la funzione di signal da <signal.h> . La dichiarazione per questo (dallo standard C) è:

void (*signal(int sig, void (*func)(int)))(int);

Questa è una funzione che accetta due argomenti: un int e un puntatore a una funzione che accetta un int come argomento e non restituisce nulla, e che restituisce un puntatore che funziona come il suo secondo argomento.

Se abbiamo definito un tipo SigCatcher come alias per il puntatore al tipo di funzione:

typedef void (*SigCatcher)(int);

quindi potremmo dichiarare signal() usando:

SigCatcher signal(int sig, SigCatcher func);

Nel complesso, questo è più facile da capire (anche se lo standard C non ha scelto di definire un tipo per fare il lavoro). La funzione signal accetta due argomenti, un int e un SigCatcher , e restituisce un SigCatcher - dove un SigCatcher è un puntatore a una funzione che accetta un argomento int e non restituisce nulla.

Sebbene l'utilizzo di nomi typedef per i tipi di puntatore a funzioni semplifica la vita, può anche portare a confusione per gli altri che manterranno il codice in un secondo momento, quindi utilizzare con cautela e documentazione adeguata. Vedi anche Function Pointers .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow