Pourquoi assert est-il une macro et non une fonction?
mon conférencier m'a demandé cela en classe, et je me demandais pourquoi c'était une macro au lieu d'une fonction?
5 réponses
la simple explication serait que la norme exige que assert
soit une macro, si nous regardons le projet de norme C99 ( autant que je peux dire les sections sont les mêmes dans projet de norme C11 ainsi que ) section 7.2
Diagnostics paragraphe 2 dit:
L'assertion de la macro doit être mis en œuvre comme un macro, pas comme un réel fonction. Si la macro définition est supprimée afin d'accéder à un fonction actuelle, le comportement n'est pas défini.
pourquoi cela est-il nécessaire, la raison donnée dans raison d'être de la norme internationale-langages de programmation-C est:
il peut être difficile ou impossible de faire affirmer une vraie fonction, donc il est limité à macro forme.
ce qui n'est pas très informatif, mais nous pouvons voir d'autres exigences pourquoi. Retour à la section 7.2
paragraphe 1 dit:
[...] Si NDEBUG est défini comme un nom macro au point du fichier source où est inclus, la macro assert est défini simplement comme
#define assert(ignore) ((void)0)
la macro affirmation est redéfinie en fonction de L'état actuel de NDEBUG chaque fois qui est inclus.
c'est important car il nous permet un moyen facile de désactiver les assertions en mode Version où vous pourriez vouloir prendre le coût de contrôles potentiellement coûteux.
et la deuxième exigence importante est qu'il est nécessaire d'utiliser les macros __FILE__
, __LINE__
et __func__
, qui est couvert dans la section 7.2.1.1
l'affirmation macro qui dit:
[...] la macro assert écrit des informations sur l'appel particulier qui a échoué [...] ces dernières sont respectivement les valeurs de prétraitement des macros _ _ fichier _ _ _ et _ _ ligne _ _ _ et de l'identificateur __func_ _) sur le flux d'erreurs standard dans un format défini par la mise en œuvre. 165) il appelle alors la fonction d'annulation.
où la note de bas de page 165
dit:
le message écrit pourrait être de la forme:
Assertion failed: expression, function abc, file xyz, line nnn.
L'avoir comme macro permet les macros __FILE__
etc... pour être évalué à l'endroit approprié et comme Joachim souligne être une macro lui permet d'insérer l'expression originale dans le message qu'il génère.
le projet de norme C++ exige que le contenu de l'en-tête cassert
soit le même que celui du assert.h
." en-tête de la bibliothèque Standrd C:
le contenu est le même que l'en-tête Standard de la bibliothèque C.
voir aussi: ISO C 7.2.
pourquoi (nul) 0?
Pourquoi utiliser (void)0
par opposition à une autre expression qui ne fait rien? Nous pouvons trouver quelques raisons, d'abord c'est comment le synopsis d'assert semble dans la rubrique 7.2.1.1
:
void assert(scalar expression);
et il dit ( c'est moi qui souligne ):
l'assert macro met les tests de diagnostic dans les programmes; il se développe à une expression de vide.
l'expression (void)0
est compatible avec la nécessité de se retrouver avec une expression vide .
en supposant que nous l'avons fait en l'absence de cette exigence, d'autres expressions possibles pourraient avoir des effets indésirables tels que permettre des utilisations de assert
en mode de publication qui ne seraient pas autorisées en mode de débogage par exemple en utilisant Uni 0
nous permettrait d'utiliser assert
dans une affectation et lorsque utilisé correctement serait susceptible de générer un avertissement expression result unused
. Quant à l'utilisation d'un énoncé composé comme un commentaire suggère, nous pouvons voir de C macro multi-ligne: do / while(0) vs scope bloque qu'ils ont des effets indésirables dans certains cas.
- il permet de capturer le fichier (par
__FILE__
) et le numéro de ligne (par__LINE__
) - il permet de substituer le
assert
à une expression valide qui ne fait rien (i.e.((void)0)
) lors de la construction en mode de libération
cette macro est désactivée si, au moment de l'inclusion , une macro avec le nom de NDEBUG a déjà été définie. Cela permet à un codeur d'inclure autant d'appels d'assert que nécessaire dans un code source pendant le débogage du programme et ensuite de les désactiver tous pour la version de production en incluant simplement une ligne comme:
#define NDEBUG
au début de son code, avant l'inclusion de <assert.h>
.
donc, cette macro est conçu pour capturer les erreurs de programmation, pas les erreurs d'utilisateur ou d'exécution, car il est généralement désactivé après qu'un programme sort de sa phase de débogage.
le faire comme fonction va augmenter certains appels de fonction et vous ne pouvez pas contrôler toutes ces assertions en mode release.
si vous utilisez la fonction, alors _FILE__
, __LINE__
et __func__
donneront la valeur du code de cette fonction d'assertion. Pas que le fait d'appeler ligne ou ligne de la fonction d'appel.
certaines assertions peuvent être coûteuses à appeler. Vous venez d'écrire une routine d'inversion matricielle haute performance, et vous ajoutez un contrôle de santé mentale
assert(is_identity(matrix * inverse))
jusqu'à la fin. Eh bien, votre matrices sont assez grandes, et si assert
est une fonction, il faudrait beaucoup de temps pour faire le calcul avant de la passer dans l'affirmer. Temps vous ne voulez vraiment pas à passer si vous ne faites pas de débogage.
Ou peut-être l'affirmation est relativement bon marché, mais il est contenu dans une fonction très courte qui sera appelée dans une boucle interne. Ou d'autres circonstances similaires.
en faisant assert
une macro à la place, vous pouvez éliminer le calcul entièrement lorsque les assertions sont désactivées.
pourquoi affirmer une macro et non une fonction?
parce qu'il devrait être compilé en mode DEBUG et ne devrait pas être compilé en mode RELEASE .