Nous avons tous définitivement utilisé typedef
s et #define
s une fois ou l'autre. Aujourd'hui, en travaillant avec eux, j'ai commencé à réfléchir à quelque chose.
Considérez les 2 situations ci-dessous pour utiliser int
le type de données avec un autre nom:
typedef int MYINTEGER
et
#define MYINTEGER int
Comme dans la situation ci-dessus, nous pouvons, dans de nombreuses situations, très bien accomplir une chose en utilisant #define, et faire la même chose en utilisant typedef, bien que les façons dont nous faisons la même chose peuvent être très différentes. #define peut également effectuer des actions MACRO qu'un typedef ne peut pas faire.
Bien que la raison fondamentale de leur utilisation soit différente, dans quelle mesure leur fonctionnement est-il différent? Quand faut-il préférer l'un à l'autre quand les deux peuvent être utilisés? De plus, est-il garanti que l'un sera plus rapide que l'autre dans quelles situations? (par exemple, #define est une directive de préprocesseur, donc tout est fait bien avant la compilation ou l'exécution).
Réponses:
A
typedef
est généralement préféré, sauf s'il existe une raison étrange pour laquelle vous avez spécifiquement besoin d'une macro.les macros font une substitution textuelle, ce qui peut faire une violence considérable à la sémantique du code. Par exemple, étant donné:
vous pourriez légalement écrire:
car se
short MYINTEGER
développe àshort int
.En revanche, avec un typedef:
le nom
MYINTEGER
est un autre nom pour le typeint
, pas une substitution textuelle pour le mot clé "int".Les choses empirent encore avec les types plus compliqués. Par exemple, étant donné ceci:
a
,b
Etc
sont tous les pointeurs, maisd
est unchar
, parce que la dernière ligne se développe à:ce qui équivaut à
(Les typedefs pour les types de pointeurs ne sont généralement pas une bonne idée, mais cela illustre le point.)
Un autre cas étrange:
la source
typedef
, mais la façon de construire une macro qui fait quelque chose avec tout ce qui suit est d'utiliser des arguments:#define CHAR_PTR(x) char *x
. Cela entraînera au moins le compilateur à kvetch s'il n'est pas utilisé correctement.const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
De loin, le plus gros problème avec les macros est qu'elles ne sont pas étendues. Cela seul justifie l'utilisation de typedef. De plus, c'est plus clair sémantiquement. Quand quelqu'un qui lit votre code voit une définition, il ne saura pas de quoi il s'agit tant qu'il ne l'a pas lu et ne comprend pas la macro entière. Un typedef indique au lecteur qu'un nom de type sera défini. (Je dois mentionner que je parle de C ++. Je ne suis pas sûr de la portée de typedef pour C, mais je suppose que c'est similaire).
la source
Le code source est principalement écrit pour les autres développeurs. Les ordinateurs en utilisent une version compilée.
Dans cette perspective, il a
typedef
un sens qui#define
ne l'est pas.la source
La réponse de Keith Thompson est très bonne et avec les points supplémentaires de Tamás Szelei sur la portée, devrait fournir tout le fond dont vous avez besoin.
Vous devez toujours considérer les macros comme le tout dernier recours des désespérés. Il y a certaines choses que vous ne pouvez faire qu'avec des macros. Même alors, vous devriez réfléchir longuement et sérieusement si vous voulez vraiment le faire. La quantité de douleur dans le débogage qui peut être causée par des macros subtilement brisées est considérable et vous obligera à parcourir les fichiers prétraités. Cela vaut la peine de le faire une seule fois, pour avoir une idée de l'étendue du problème en C ++ - la taille du fichier prétraité peut être révélatrice.
la source
En plus de toutes les remarques valables sur la portée et le remplacement de texte déjà données, #define n'est pas non plus entièrement compatible avec typedef! À savoir lorsqu'il s'agit du cas des pointeurs de fonction.
Cela déclare un type
fptr_t
qui est un pointeur vers une fonction du typevoid func(void)
.Vous ne pouvez pas déclarer ce type avec une macro.
#define fptr_t void(*)(void)
ne fonctionnera évidemment pas. Il faudrait écrire quelque chose d'obscur comme#define fptr_t(name) void(*name)(void)
, ce qui n'a vraiment aucun sens dans le langage C, où nous n'avons pas de constructeurs.Les pointeurs de tableau ne peuvent pas non plus être déclarés avec #define:
typedef int(*arr_ptr)[10];
Et bien que C n'ait pas de prise en charge linguistique pour la sécurité des types à noter, un autre cas où typedef et #define ne sont pas compatibles est lorsque vous effectuez des conversions de types suspectes. Le compilateur et / ou l'outil d'analyseur astatique peuvent être en mesure de donner des avertissements pour de telles conversions si vous utilisez typedef.
la source
char *(*(*foo())[])();
(foo
est une fonction renvoyant un pointeur vers un tableau de pointeurs vers des fonctions renvoyant le pointeurchar
).Utilisez l'outil avec le moins de puissance pour faire le travail et celui avec le plus d'avertissements. #define est évalué dans le préprocesseur, vous y êtes largement seul. typedef est évalué par le compilateur. Des vérifications sont données et typedef ne peut définir que des types, comme son nom l'indique. Donc, dans votre exemple, choisissez définitivement typedef.
la source
Au-delà des arguments normaux contre define, comment écririez-vous cette fonction à l'aide de macros?
C'est évidemment un exemple trivial, mais cette fonction peut résumer n'importe quelle séquence itérative vers l'avant d'un type qui implémente l'addition *. Les typedefs utilisés comme celui-ci sont un élément important de la programmation générique .
* Je viens d'écrire ceci ici, je laisse le compiler comme un exercice pour le lecteur :-)
MODIFIER:
Cette réponse semble être source de beaucoup de confusion, alors permettez-moi de la développer davantage. Si vous regardiez à l'intérieur de la définition d'un vecteur STL, vous verriez quelque chose de similaire à ce qui suit:
L'utilisation de typedefs dans les conteneurs standard permet à une fonction générique (comme celle que j'ai créée ci-dessus) de référencer ces types. La fonction "Sum" est basée sur le type du conteneur (
std::vector<int>
), pas sur le type contenu à l'intérieur du conteneur (int
). Sans typedef, il ne serait pas possible de se référer à ce type interne.Par conséquent, les typedefs sont au cœur de Modern C ++ et cela n'est pas possible avec les macros.
la source
typedef
, et non sur les fonctions macros vs inline.typedef
correspond à la philosophie C ++: toutes les vérifications / assertions possibles en temps de compilation.#define
est juste une astuce de préprocesseur qui cache beaucoup de sémantique au compilateur. Vous ne devriez pas vous soucier des performances de compilation plus que de l'exactitude du code.Lorsque vous créez un nouveau type, vous définissez une nouvelle «chose» que le domaine de votre programme manipule. Vous pouvez donc utiliser cette «chose» pour composer des fonctions et des classes, et vous pouvez utiliser le compilateur à votre avantage pour effectuer des vérifications statiques. Quoi qu'il en soit, comme C ++ est compatible avec C, il existe de nombreuses conversions implicites entre les
int
types basés sur et int qui ne génèrent pas d'avertissements. Ainsi, vous n'obtenez pas la pleine puissance des vérifications statiques dans ce cas. Cependant, vous pouvez remplacer votretypedef
par unenum
pour rechercher des conversions implicites. Par exemple: si vous l'avez faittypedef int Age;
, vous pouvez le remplacer parenum Age { };
et vous obtiendrez toutes sortes d'erreurs par des conversions implicites entreAge
etint
.Une autre chose: le
typedef
peut être à l'intérieur d'unnamespace
.la source
L'utilisation de définitions au lieu de typedefs est troublante sous un autre aspect important, à savoir le concept de traits de type. Considérez différentes classes (pensez aux conteneurs standard) qui définissent toutes leurs typedefs spécifiques. Vous pouvez écrire du code générique en vous référant aux typedefs. Des exemples de cela incluent les exigences générales du conteneur (norme c ++ 23.2.1 [container.requirements.general]) comme
Tout cela n'est pas exprimable de manière générique avec une macro car il n'est pas délimité.
la source
N'oubliez pas les implications de cela dans votre débogueur. Certains débogueurs ne gèrent pas très bien #define. Jouez avec les deux dans le débogueur que vous utilisez. N'oubliez pas que vous passerez plus de temps à le lire qu'à l'écrire.
la source
"#define" remplacera ce que vous avez écrit après, typedef créera du type. Donc, si vous voulez avoir un type personnalisé, utilisez typedef. Si vous avez besoin de macros - utilisez define.
la source