Mon conférencier m'a demandé cela en classe et je me demandais pourquoi est-ce une macro plutôt qu'une fonction?
Réponses
Trop de publicités?L'explication simple serait que la norme exige assert
à être d'une macro, si l'on regarde le projet de standard C99(autant que je peux dire que les sections sont les mêmes dans le projet de norme C11 ainsi) section 7.2
des Diagnostics de l'alinéa 2 dit:
L'assertion de la macro doit être mis en œuvre comme une macro, pas comme une réelle fonction. Si la définition de la macro est supprimée afin d'accéder à un d'une fonction réelle, le comportement est indéfini.
Pourquoi faut-il de cette, la justification donnée dans la Justification de la Norme Internationale-Langages de Programmation-C est:
Il peut être difficile ou impossible de faire valoir une vraie fonction, de sorte qu'il est limité à la macro forme.
ce qui n'est pas très instructif, mais nous pouvons le voir sur d'autres exigences de la raison. Allez, retour à la section 7.2
le paragraphe 1 , dit:
[...]Si NDEBUG est défini comme un nom de macro au point dans le fichier source où est inclus, l'assertion de la macro est définie simplement comme
#define assert(ignore) ((void)0)
La macro assert est redéfini en fonction de l'état actuel de NDEBUG chaque fois que est inclus.
Ceci est important car il nous permet de nous un moyen facile de désactiver les assertions contenues dans le communiqué de mode où vous pouvez prendre le coût de potentiellement coûteux contrôles.
et la deuxième exigence, c'est qu'il est nécessaire d'utiliser les macros __FILE__
, __LINE__
et __func__
, ce qui est couvert dans la section 7.2.1.1
La macro assert qui dit:
[...] la macro assert écrit les informations sur l'appel particulier qui a échoué [...] ces derniers sont respectivement les valeurs de la prétraitement des macros __FILE_ _ et __LINE_ _, et de l'identifiant __func_ _) sur le flux d'erreur standard dans une mise en œuvre définies par le format.165) Il appelle ensuite la fonction d'annulation.
où la note de bas de page 165
dit:
Le message peut être de la forme:
Assertion failed: expression, function abc, file xyz, line nnn.
Ayant comme macro permet de macros __FILE__
etc... pour être évalué dans l'emplacement approprié et que Joachim points d'être un macro permet d'insérer l'origine de l'expression dans le message qu'il génère.
Le projet de norme C++ exige que le contenu de l' cassert
d'en-tête sont les mêmes que l' assert.h
- tête de Standrd de la bibliothèque C:
Le contenu est le même que la bibliothèque Standard C en-tête .
Voir aussi: ISO C 7.2.
Pourquoi (void)0?
Pourquoi utiliser (void)0
plutôt que d'une autre expression qui ne fait rien? Nous pouvons venir avec quelques raisons, la première c'est comment l'affirmer synopsis regarde dans la section 7.2.1.1
:
void assert(scalar expression);
et il dit (c'est moi qui souligne):
La macro assert met des tests de diagnostic dans les programmes; il s'étend à un vide d'expression.
l'expression (void)0
est compatible avec la nécessité de se retrouver avec un vide d'expression.
En supposant que nous n'avons pas cette exigence, d'autres expressions possibles pourraient avoir des effets indésirables, tels que permettre à des utilisations de l' assert
en mode release, qui ne serait pas autorisée en mode debug par exemple à l'aide de plain 0
nous permettant d'utiliser assert
dans une affectation et lorsqu'il est utilisé correctement serait susceptible de générer un expression result unused
d'avertissement. Comme pour l'utilisation d'une instruction composée comme un commentaire suggère, nous pouvons voir à partir de C multi-ligne de macro: do/while(0) vs portée bloc qu'ils ont des effets indésirables dans certains cas.
Cette macro est désactivé si, au moment de l'inclusion , une macro avec le nom NDEBUG a déjà été défini. Cela permet, pour un codeur pour comprendre autant d'affirmer appels que nécessaire dans un code source pendant le débogage du programme et ensuite désactiver l'ensemble de la mise en production de la version en insérant simplement une ligne comme:
#define NDEBUG
au début de son code, avant l'inclusion de l' <assert.h>
.
Par conséquent, cette macro est conçu pour la capture des erreurs de programmation, pas de l'utilisateur ou des erreurs d'exécution, puisqu'il est généralement désactivée après un programme sort de sa phase de débogage.
Faire fonction permet d'augmenter certains appels de fonction et vous ne pouvez pas contrôler toutes ces affirme en mode release.
Si vous utilisez la fonction d' _FILE__
, __LINE__
et __func__
donnera la valeur de cette assertion du code de fonction. Non pas que l'appel de la ligne ou de l'appel de la fonction de la ligne.
Certaines affirmations peuvent être coûteux à l'appel. Vous avez juste écrit une haute performance d'inversion de matrice de routine, et vous ajoutez un contrôle de votre site
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 c'est contenue dans un très court laps de fonction qui sera appelée dans une boucle interne. Ou d'autres circonstances similaires.
En rendant assert
d'une macro au lieu de cela, vous pouvez éliminer le calcul entièrement lorsque les assertions sont éteints.