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. ;)
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.
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 typedef
s.
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.
Il y a plusieurs raisons à cela. Ce que je pense c'est:
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.
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.
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.
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.
#define BEGIN {
sauf qu'il enregistre un peu de frappe au lieu d'en faire plus.#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.typedef uint32_t u32;
ou mieux encore, uint32_t
doit être utilisé directement.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.
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.
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.
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.