J'ai toujours vu des exemples et des cas où l'utilisation d'une macro est meilleure que l'utilisation d'une fonction.
Quelqu'un pourrait-il m'expliquer avec un exemple l'inconvénient d'une macro par rapport à une fonction?
c
function
c-preprocessor
Kyrol
la source
la source
Réponses:
Les macros sont sujettes aux erreurs car elles reposent sur la substitution textuelle et n'effectuent pas de vérification de type. Par exemple, cette macro:
fonctionne bien lorsqu'il est utilisé avec un entier:
mais fait des choses très étranges lorsqu'il est utilisé avec des expressions:
Mettre des parenthèses autour des arguments aide mais n'élimine pas complètement ces problèmes.
Lorsque les macros contiennent plusieurs instructions, vous pouvez avoir des problèmes avec les constructions de flux de contrôle:
La stratégie habituelle pour résoudre ce problème est de placer les instructions dans une boucle "do {...} while (0)".
Si vous avez deux structures qui contiennent un champ avec le même nom mais une sémantique différente, la même macro peut fonctionner sur les deux, avec des résultats étranges:
Enfin, les macros peuvent être difficiles à déboguer, produisant des erreurs de syntaxe étranges ou des erreurs d'exécution que vous devez développer pour comprendre (par exemple avec gcc -E), car les débogueurs ne peuvent pas parcourir les macros, comme dans cet exemple:
Les fonctions et les constantes en ligne permettent d'éviter beaucoup de ces problèmes avec les macros, mais ne sont pas toujours applicables. Lorsque des macros sont délibérément utilisées pour spécifier un comportement polymorphe, le polymorphisme involontaire peut être difficile à éviter. C ++ a un certain nombre de fonctionnalités telles que des modèles pour aider à créer des constructions polymorphes complexes d'une manière sécurisée sans l'utilisation de macros; voir le langage de programmation C ++ de Stroustrup pour plus de détails.
la source
x++*x++
ne peut donc pas dire que l'expression s'incrémentex
deux fois; il invoque en fait un comportement indéfini , ce qui signifie que le compilateur est libre de faire tout ce qu'il veut - il peut s'incrémenterx
deux fois, une fois ou pas du tout; il pourrait avorter avec une erreur ou même faire voler des démons hors de votre nez .Caractéristiques macro :
Caractéristiques de la fonction :
la source
Les effets secondaires sont importants. Voici un cas typique:
est étendu à:
x
est incrémenté deux fois dans la même instruction. (et comportement indéfini)L'écriture de macros multilignes est également une douleur:
Ils nécessitent un
\
à la fin de chaque ligne.Les macros ne peuvent rien "renvoyer" à moins que vous n'en fassiez une seule expression:
Vous ne pouvez pas faire cela dans une macro à moins que vous n'utilisiez l'instruction d'expression de GCC. (EDIT: Vous pouvez cependant utiliser un opérateur virgule ... j'ai oublié cela ... Mais cela pourrait encore être moins lisible.)
Ordre des opérations: (avec l'aimable autorisation de @ouah)
est étendu à:
Mais
&
a une priorité inférieure à<
. Donc0xFF < 42
est évalué en premier.la source
min(a & 0xFF, 42)
Exemple 1:
tandis que:
Exemple 2:
Par rapport à:
la source
En cas de doute, utilisez des fonctions (ou des fonctions en ligne).
Cependant, les réponses ici expliquent principalement les problèmes avec les macros, au lieu d'avoir une idée simple que les macros sont mauvaises parce que des accidents stupides sont possibles.
Vous pouvez être conscient des pièges et apprendre à les éviter. Utilisez ensuite les macros uniquement lorsqu'il y a une bonne raison de le faire.
Il existe certains cas exceptionnels où l'utilisation de macros présente des avantages, notamment:
va_args
.Par exemple: https://stackoverflow.com/a/24837037/432509 .
(
__FILE__
,__LINE__
,__func__
). vérifier les conditions pré / post,assert
en cas d'échec ou même d'assertions statiques pour que le code ne se compile pas en cas d'utilisation incorrecte (surtout utile pour les versions de débogage).struct
membres sont présents avant la conversion(peut être utile pour les types polymorphes) .
Ou vérifiez qu'un tableau remplit une condition de longueur.
voir: https://stackoverflow.com/a/29926435/432509
func(FOO, "FOO");
, vous pouvez définir une macro qui développe la chaîne pour vousfunc_wrapper(FOO);
(les affectations à plusieurs variables, pour une opération par pixel, est un exemple que vous pourriez préférer une macro à une fonction ... même si cela dépend toujours beaucoup du contexte, car les
inline
fonctions peuvent être une option) .Certes, certains d'entre eux reposent sur des extensions de compilateur qui ne sont pas standard C. Cela signifie que vous pouvez vous retrouver avec moins de code portable, ou en avoir besoin
ifdef
, donc ils ne sont exploités que lorsque le compilateur prend en charge.Éviter l'instanciation d'arguments multiples
Noter cela car c'est l'une des causes les plus courantes d'erreurs dans les macros (en passant
x++
par exemple, où une macro peut s'incrémenter plusieurs fois) .il est possible d'écrire des macros qui évitent les effets secondaires avec plusieurs instanciations d'arguments.
Générique C11
Si vous aimez avoir une
square
macro qui fonctionne avec différents types et qui prend en charge C11, vous pouvez le faire ...Expressions de déclaration
Il s'agit d'une extension de compilateur supportée par GCC, Clang, EKOPath et Intel C ++ (mais pas MSVC) ;
Donc, l'inconvénient des macros est que vous devez savoir comment les utiliser pour commencer et qu'elles ne sont pas prises en charge aussi largement.
Un avantage est que, dans ce cas, vous pouvez utiliser la même
square
fonction pour de nombreux types différents.la source
Aucune vérification de type des paramètres et du code n'est répétée, ce qui peut entraîner un gonflement du code. La syntaxe de macro peut également conduire à n'importe quel nombre de cas de bord étranges où les points-virgules ou l'ordre de priorité peuvent gêner. Voici un lien qui démontre un mal macro
la source
un inconvénient des macros est que les débogueurs lisent le code source, qui n'a pas de macros développées, donc exécuter un débogueur dans une macro n'est pas nécessairement utile. Inutile de dire que vous ne pouvez pas définir un point d'arrêt dans une macro comme vous le pouvez avec des fonctions.
la source
Les fonctions vérifient le type. Cela vous donne une couche supplémentaire de sécurité.
la source
Ajout à cette réponse…
Les macros sont remplacées directement dans le programme par le préprocesseur (car ce sont essentiellement des directives de préprocesseur). Ils utilisent donc inévitablement plus d'espace mémoire qu'une fonction respective. D'autre part, une fonction nécessite plus de temps pour être appelée et pour renvoyer des résultats, et cette surcharge peut être évitée en utilisant des macros.
Les macros ont également des outils spéciaux qui peuvent aider à la portabilité des programmes sur différentes plates-formes.
Les macros n'ont pas besoin de se voir attribuer un type de données pour leurs arguments contrairement aux fonctions.
Dans l'ensemble, ils sont un outil utile dans la programmation. Et les macro-instructions et les fonctions peuvent être utilisées selon les circonstances.
la source
Je n'ai pas remarqué, dans les réponses ci-dessus, un avantage des fonctions par rapport aux macros que je pense être très important:
Les fonctions peuvent être passées en arguments, les macros ne le peuvent pas.
Exemple concret: vous souhaitez écrire une version alternative de la fonction standard 'strpbrk' qui acceptera, plutôt qu'une liste explicite de caractères à rechercher dans une autre chaîne, une fonction (pointeur vers une) qui renverra 0 jusqu'à ce qu'un caractère soit trouvé qui passe un certain test (défini par l'utilisateur). Une des raisons pour lesquelles vous voudrez peut-être faire cela est de pouvoir exploiter d'autres fonctions de bibliothèque standard: au lieu de fournir une chaîne explicite pleine de ponctuation, vous pouvez passer 'ispunct' de ctype.h à la place, etc. Si 'ispunct' a été implémenté uniquement comme une macro, cela ne fonctionnerait pas.
Il existe de nombreux autres exemples. Par exemple, si votre comparaison est effectuée par macro plutôt que par fonction, vous ne pouvez pas la passer au 'qsort' de stdlib.h.
Une situation analogue en Python est «imprimer» dans la version 2 par rapport à la version 3 (instruction non passable vs fonction passable).
la source
Si vous passez la fonction en tant qu'argument à la macro, elle sera évaluée à chaque fois. Par exemple, si vous appelez l'une des macros les plus populaires:
comme ça
functionThatTakeLongTime sera évalué 5 fois, ce qui peut réduire considérablement les performances
la source