Recherche…


Introduction

Le mécanisme typedef permet la création d'alias pour d'autres types. Il ne crée pas de nouveaux types. Les personnes utilisent souvent typedef pour améliorer la portabilité du code, pour donner des alias à des types de structure ou d'union, ou pour créer des alias pour les types de fonctions (ou de pointeurs de fonctions).

Dans la norme C, typedef est classé comme une «classe de stockage» par commodité; il se produit de manière syntaxique lorsque des classes de stockage telles que static ou extern peuvent apparaître.

Syntaxe

  • typedef nom_existant nom_alias;

Remarques


Inconvénients de Typedef

typedef pourrait conduire à la pollution de l'espace de nommage dans les grands programmes C.

Inconvénients des structures Typedef

De plus, les structures typedef sans nom de tag sont une cause majeure de l'imposition inutile de relations d'ordre entre les fichiers d'en-tête.

Considérer:

#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

Avec une telle définition, n'utilisant pas les typedefs , il est possible qu'une unité de compilation inclue foo.h pour obtenir la définition de FOO_DEF . S'il ne tente pas de déréférencer le membre bar de la structure foo il ne sera pas nécessaire d'inclure le fichier bar.h

Typedef vs #define

#define est une directive de préprocesseur C qui est également utilisée pour définir les alias pour différents types de données similaires à typedef mais avec les différences suivantes:

  • typedef se limite à donner des noms symboliques aux types uniquement où #define peut également être utilisé pour définir des alias pour les valeurs.

  • typedef interprétation typedef est effectuée par le compilateur alors que les instructions #define sont traitées par le pré-processeur.

  • Notez que #define cptr char * suivi de cptr a, b; ne fait pas la même chose que typedef char *cptr; suivi de cptr a, b; . Avec #define , b est une variable char ordinaire, mais c'est aussi un pointeur avec le typedef .

Typedef pour les structures et les syndicats

Vous pouvez donner des noms d'alias à une struct :

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

Person person;

Comparé à la manière traditionnelle de déclarer des structures, les programmeurs n'auraient pas besoin de struct chaque fois qu'ils déclarent une instance de cette structure.

Notez que le nom de Person (par opposition à struct Person ) n'est défini qu'au dernier point-virgule. Ainsi, pour les listes liées et les arborescences qui doivent contenir un pointeur vers le même type de structure, vous devez utiliser soit:

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

ou:

typedef struct Person Person;

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

L'utilisation d'un typedef pour un type d' union est très similaire.

typedef union Float Float;

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

Une structure similaire à celle-ci peut être utilisée pour analyser les octets qui constituent une valeur float .

Utilisations simples de Typedef

Pour donner des noms courts à un type de données

Au lieu de:

long long int foo;
struct mystructure object;

on peut utiliser

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

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

Cela réduit la quantité de frappe nécessaire si le type est utilisé plusieurs fois dans le programme.

Améliorer la portabilité

Les attributs des types de données varient selon les architectures. Par exemple, un int peut être un type à 2 octets dans une implémentation et un type à 4 octets dans un autre. Supposons qu'un programme doive utiliser un type de 4 octets pour s'exécuter correctement.

Dans une implémentation, la taille de int être de 2 octets et celle de long 4 octets. Dans un autre, laissez la taille de int être de 4 octets et celle de long 8 octets. Si le programme est écrit en utilisant la deuxième implémentation,

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

Pour que le programme s'exécute dans la première implémentation, toutes les déclarations int devront être modifiées en long .

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

Pour éviter cela, on peut utiliser 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 */

Ensuite, seule l'instruction typedef devrait être modifiée à chaque fois, au lieu d'examiner l'ensemble du programme.

C99

L'en-tête <stdint.h> et l'en-tête associé <inttypes.h> définissent les noms de type standard (en utilisant typedef ) pour les entiers de différentes tailles. Par exemple, uint8_t est un type entier non signé de 8 bits; int64_t est un type entier signé de 64 bits. Le type uintptr_t est un type entier non signé suffisamment grand pour contenir un pointeur sur un objet. Ces types sont théoriquement facultatifs - mais il est rare qu'ils ne soient pas disponibles. Il existe des variantes comme uint_least16_t (le plus petit type entier non signé avec au moins 16 bits) et int_fast32_t (le type entier signé le plus rapide avec au moins 32 bits). En outre, intmax_t et uintmax_t sont les plus grands types d'entiers pris en charge par l'implémentation. Ces types sont obligatoires.

Pour spécifier un usage ou améliorer la lisibilité

Si un ensemble de données a un objectif particulier, on peut utiliser typedef pour lui donner un nom significatif. De plus, si la propriété des données change de telle sorte que le type de base doit changer, seule l'instruction typedef devra être modifiée, au lieu d'examiner l'ensemble du programme.

Typedef pour les pointeurs de fonction

Nous pouvons utiliser typedef pour simplifier l'utilisation des pointeurs de fonctions. Imaginez que nous ayons des fonctions, toutes ayant la même signature, qui utilisent leur argument pour imprimer quelque chose de différentes manières:

#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);
}

Maintenant, nous pouvons utiliser un typedef pour créer un type de pointeur de fonction nommé appelé imprimante:

typedef void (*printer_t)(int);

Cela crée un type, nommé printer_t pour un pointeur sur une fonction qui prend un seul argument int et ne renvoie rien, ce qui correspond à la signature des fonctions ci-dessus. Pour l'utiliser, nous créons une variable du type créé et lui assignons un pointeur sur l'une des fonctions en question:

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

Ensuite, appeler la fonction pointée par la variable de pointeur de fonction:

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

Ainsi, typedef permet une syntaxe plus simple lorsqu'il s'agit de pointeurs de fonctions. Cela devient plus évident lorsque des pointeurs de fonction sont utilisés dans des situations plus complexes, telles que des arguments pour des fonctions.

Si vous utilisez une fonction qui prend un pointeur de fonction comme paramètre sans type de pointeur de fonction défini, la définition de fonction serait,

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

Cependant, avec le typedef c'est:

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

De même, les fonctions peuvent renvoyer des pointeurs de fonction et, à nouveau, l'utilisation d'un typedef peut simplifier la syntaxe.

Un exemple classique est la fonction de signal de <signal.h> . La déclaration pour cela (du standard C) est:

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

C'est une fonction qui prend deux arguments - un int et un pointeur sur une fonction qui prend un int comme argument et ne retourne rien - et qui renvoie un pointeur pour fonctionner comme son deuxième argument.

Si nous avons défini un type SigCatcher comme alias pour le pointeur sur le type de fonction:

typedef void (*SigCatcher)(int);

alors nous pourrions déclarer le signal() utilisant:

SigCatcher signal(int sig, SigCatcher func);

Dans l'ensemble, cela est plus facile à comprendre (même si la norme C n'a pas choisi de définir un type pour effectuer le travail). La fonction signal prend deux arguments, un int et un SigCatcher , et renvoie un SigCatcher - où un SigCatcher est un pointeur sur une fonction qui prend un argument int et ne retourne rien.

Bien que l'utilisation de noms de type typedef pour les types de pointeurs vers les fonctions facilite la vie, cela peut également être source de confusion pour les autres utilisateurs qui conserveront votre code ultérieurement. À utiliser avec précaution et avec une documentation appropriée. Voir aussi les pointeurs de fonction .



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow