web-dev-qa-db-fra.com

Pourquoi les programmeurs C utilisent-ils des typedefs pour renommer les types de base?

Je suis donc loin d'être un expert en C, mais quelque chose me dérange sur le code que je lis depuis longtemps: quelqu'un peut-il m'expliquer pourquoi les programmeurs C (++) utilisent des typedefs pour renommer des types simples? Je comprends pourquoi vous les utiliseriez pour des structures, mais quelle est exactement la raison des déclarations que je vois comme

typedef unsigned char uch;
typedef uch UBYTE;
typedef unsigned long ulg;
typedef unsigned int u32;
typedef signed short s16;

Y a-t-il un avantage qui n'est pas clair pour moi (un programmeur dont l'expérience commence par Java et qui ne s'est pas aventuré loin en dehors des langages strictement sécurisés)? Parce que je ne peux pas pensez à n'importe quelle raison - il semble que cela rendrait le code moins lisible pour les personnes peu familières avec le projet.

N'hésitez pas à me traiter comme un débutant en C, honnêtement j'en sais très peu à ce sujet et il est probable qu'il y ait des choses que j'ai mal comprises depuis le début. ;)

54

Renommer des types sans modifier leur sémantique/caractéristiques exposées n'a pas beaucoup de sens. Dans votre exemple

typedef unsigned char uch;
typedef unsigned long ulg;

appartiennent à cette catégorie. Je ne vois pas l'intérêt, à part faire un nom plus court.

Mais ceux-là

typedef uch UBYTE;
typedef unsigned int u32;
typedef signed short s16;

sont une histoire complètement différente. Par exemple, s16 signifie "type 16 bits signé". Ce type n'est pas nécessairement signed short. Quel type spécifique se cache derrière s16 dépend de la plate-forme. Les programmeurs introduisent ce niveau supplémentaire d'indirection de dénomination pour simplifier la prise en charge de plusieurs plates-formes. Si sur une autre plateforme, le type 16 bits signé se trouve être signed int, le programmeur n'aura qu'à modifier une définition de typedef. UBYTE représente apparemment un type d'octet de machine non signé, qui n'est pas nécessairement unsigned char.

Il convient de noter que la spécification C99 fournit déjà une nomenclature standard pour les types intégraux de largeur spécifique, comme int16_t, uint32_t etc. Il est probablement plus logique de s'en tenir à cette convention de dénomination standard sur les plates-formes qui ne prennent pas en charge C99.

78
AnT

Cela permet la portabilité. Par exemple, vous avez besoin d'un type entier 32 bits non signé. De quel type standard s'agit-il? Vous ne savez pas - c'est l'implémentation définie. C'est pourquoi vous typedef un type distinct pour être un entier non signé 32 bits et utilisez le nouveau type dans votre code. Lorsque vous devez compiler sur une autre implémentation C, vous changez simplement les typedefs.

14
sharptooth

Parfois, il est utilisé pour réduire une chose lourde comme volatile unsigned long à quelque chose d'un peu plus compact comme vuint32_t.

D'autres fois, c'est pour aider à la portabilité car les types comme int ne sont pas toujours les mêmes sur chaque plate-forme. En utilisant un typedef, vous pouvez définir la classe de stockage qui vous intéresse sur la correspondance la plus proche de la plate-forme sans changer tout le code source.

8
Amardeep AC9MF

Il y a plusieurs raisons à cela. Ce que je pense c'est:

  1. Le nom de type devient plus court et donc le code est également plus petit et plus lisible.
  2. Effet d'alias pour des noms de structure plus longs.
  3. Convention utilisée en particulier équipe/entreprises/style.
  4. Portage - Ont le même nom sur tous les systèmes d'exploitation et toutes les machines. Sa structure de données native peut être légèrement différente.
4
Jack

Voici une citation du langage de programmation C (K&R)

Outre les problèmes purement esthétiques, il existe deux raisons principales d'utiliser des typedefs.

Premier à paramétrer un programme

La première consiste à paramétrer un programme contre les problèmes de portabilité. Si des typedefs sont utilisés pour des types de données qui peuvent être dépendants de la machine, seuls les typedefs doivent être modifiés lorsque le programme est déplacé.

ne situation courante consiste à utiliser des noms de typedef pour différentes quantités entières, puis à faire un ensemble approprié de choix de short, int et long pour chaque machine hôte. Des types comme size_t et ptrdiff_t de la bibliothèque standard sont des exemples .

Les parties en italique nous indiquent que les programmeurs typedef type de base pour la portabilité. Si je veux m'assurer que mon programme fonctionne sur différentes plates-formes, en utilisant un compilateur différent, je vais essayer de m'assurer que sa portabilité de toutes les manières possibles et typedef en fait partie.

Quand j'ai commencé à programmer en utilisant le compilateur Turbo C sur la plate-forme Windows, cela nous donnait la taille de int 2. Lorsque je suis passé à la plate-forme Linux et à GCC complier, la taille que j'obtiens est de 4. Si j'avais développé un programme utilisant Turbo C qui s'appuyait sur l'affirmation que sizeof( int ) est toujours deux, il ne se serait pas porté correctement sur ma nouvelle plateforme.

J'espère que cela aide.

La citation suivante de K&R n'est pas liée à votre requête, mais je l'ai également publiée dans un souci de finalisation.

Deuxièmement - pour fournir une meilleure documentation

Le deuxième objectif des typedefs est de fournir une meilleure documentation pour un programme - un type appelé Treeptr peut être plus facile à comprendre que celui déclaré uniquement comme un pointeur vers une structure compliquée.

4
Andrew-Dufresne

La plupart de ces modèles sont de mauvaises pratiques qui proviennent de la lecture et de la copie de mauvais code existant. Souvent, ils reflètent des malentendus sur ce que C requiert ou n'exige pas.

  1. Est semblable à #define BEGIN { sauf qu'il enregistre un peu de frappe au lieu d'en faire plus.
  2. Est semblable à #define FALSE 0. Si votre idée de "byte" est la plus petite unité adressable, char est un octet par définition. Si votre idée de "byte" est un octet, alors char est le type d'octet, ou votre machine n'a pas de type d'octet.
  3. C'est un raccourci vraiment moche pour les personnes qui ne peuvent pas toucher le type ...
  4. Est une erreur. Ça devrait être typedef uint32_t u32; ou mieux encore, uint32_t doit être utilisé directement.
  5. Est identique à 4. Remplacez uint32_t avec int16_t.

Veuillez apposer un cachet "considéré comme dangereux" sur tous. typedef doit être utilisé lorsque vous avez vraiment besoin de créer un nouveau type dont la définition pourrait changer au cours du cycle de vie de votre code ou lorsque le code est porté sur un matériel différent, pas parce que vous pensez que C serait "plus joli" avec noms de types différents.

3
R..

Tout [|u]intN_t types, où N = 8 | 16 | 32 | 64 et ainsi de suite, sont définis par architecture de cette manière exacte. C'est une conséquence directe du fait que la norme n'exige pas que char, int, float, etc. aient exactement N bits - ce serait insensé. Au lieu de cela, la norme définit des valeurs minimales et maximales de chaque type comme garanties pour le programmeur, et dans diverses architectures, les types peuvent bien dépasser ces limites. Ce n'est pas un spectacle rare.

Les typedef dans votre publication sont utilisés pour définir des types d'une certaine longueur, dans une architecture spécifique. Ce n'est probablement pas le meilleur choix de dénomination; u32 et s16 sont un peu trop courts, à mon avis. En outre, c'est un peu une mauvaise chose d'exposer les noms ulg et uch, on pourrait les préfixer avec une chaîne spécifique à l'application car ils ne seront évidemment pas exposés.

J'espère que cela t'aides.

0
Michael Foukarakis

Nous l'utilisons pour le rendre spécifique au projet/à la plate-forme, tout a une convention de dénomination commune

pname_int32, pname_uint32, pname_uint8 - pname est le nom du projet/de la plateforme/du module

Et quelques #defines

pname_malloc, pname_strlen

Il est plus facile à lire et raccourcit les types de données longs comme char non signé à pname_uint8, ce qui en fait également une convention sur tous les modules.

Lors du portage, vous devez simplement modifier le fichier unique, ce qui facilite le portage.

0
Praveen S

Pour raccourcir la longue histoire, vous voudrez peut-être le faire pour rendre votre code portable (avec moins d'effort/d'édition). De cette façon, vous ne dépendez pas de "int", au lieu de cela, vous utilisez INTEGER qui peut être tout ce que vous voulez.

0
houman001