C Language
Affirmation
Recherche…
Introduction
Une assertion est un prédicat que la condition présentée doit être vraie au moment où l'assertion est rencontrée par le logiciel. Les plus simples sont les assertions simples , validées au moment de l'exécution. Cependant, les assertions statiques sont vérifiées au moment de la compilation.
Syntaxe
- affirmer (expression)
- static_assert (expression, message)
- _Static_assert (expression, message)
Paramètres
Paramètre | Détails |
---|---|
expression | expression de type scalaire. |
message | chaîne littérale à inclure dans le message de diagnostic. |
Remarques
Les deux assert
et static_assert
sont des macros définies dans assert.h
.
La définition de assert
dépend de la macro NDEBUG
qui n'est pas définie par la bibliothèque standard. Si NDEBUG
est défini, assert
est un no-op:
#ifdef NDEBUG # define assert(condition) ((void) 0) #else # define assert(condition) /* implementation defined */ #endif
L'opinion varie selon que NDEBUG
doit toujours être utilisé pour les compilations de production.
- Le pro-camp soutient que
assert
appelsabort
et les messages d'affirmation ne sont pas utiles pour les utilisateurs finaux, de sorte que le résultat n'est pas utile à l' utilisateur. Si vous avez des conditions fatales à vérifier dans le code de production, vous devez utiliser lesif/else
ordinaires etexit
ouquick_exit
pour terminer le programme. Contrairement à l’abort
, elles permettent au programme de faire du nettoyage (via des fonctions enregistrées avecatexit
ouat_quick_exit
). - Le con camp fait valoir que
assert
appels ne doivent jamais tirer dans le code de production, mais si elles le font, la condition est cochée, il y a quelque chose de mal et de façon spectaculaire le programme se conduisent mal pire si l' exécution se poursuit. Par conséquent, il est préférable d’avoir des assertions actives dans le code de la production car si elles se déclenchent, l’enfer est déjà déchaîné. - Une autre option consiste à utiliser un système d'assertions maison qui exécute toujours la vérification mais gère les erreurs différemment entre le développement (lorsque l'
abort
est approprié) et la production (où une «erreur interne inattendue - veuillez contacter le support technique» peut être plus appropriée).
static_assert
développe en _Static_assert
qui est un mot clé. La condition est vérifiée au moment de la compilation, la condition
doit donc être une expression constante. Il n'est pas nécessaire que cela soit traité différemment entre le développement et la production.
Condition préalable et postcondition
Un cas d'utilisation pour l'assertion est la condition préalable et la post-condition. Cela peut être très utile pour maintenir invariant et concevoir par contrat . Pour un exemple, une longueur est toujours nulle ou positive, donc cette fonction doit renvoyer une valeur nulle ou positive.
#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>
int length2 (int *a, int count)
{
int i, result = 0;
/* Precondition: */
/* NULL is an invalid vector */
assert (a != NULL);
/* Number of dimensions can not be negative.*/
assert (count >= 0);
/* Calculation */
for (i = 0; i < count; ++i)
{
result = result + (a[i] * a[i]);
}
/* Postcondition: */
/* Resulting length can not be negative. */
assert (result >= 0);
return result;
}
#define COUNT 3
int main (void)
{
int a[COUNT] = {1, 2, 3};
int *b = NULL;
int r;
r = length2 (a, COUNT);
printf ("r = %i\n", r);
r = length2 (b, COUNT);
printf ("r = %i\n", r);
return 0;
}
Assertion simple
Une assertion est une déclaration utilisée pour affirmer qu'un fait doit être vrai lorsque cette ligne de code est atteinte. Les assertions sont utiles pour s'assurer que les conditions attendues sont remplies. Lorsque la condition passée à une assertion est vraie, il n'y a pas d'action. Le comportement sur les conditions fausses dépend des indicateurs du compilateur. Lorsque des assertions sont activées, une fausse entrée provoque un arrêt immédiat du programme. Lorsqu'elles sont désactivées, aucune action n'est entreprise. Il est courant d'activer les assertions dans les versions internes et de débogage, et de les désactiver dans les versions de publication, bien que les assertions soient souvent activées dans la version. (Le fait que la terminaison soit meilleure ou pire que les erreurs dépend du programme.) Les assertions ne doivent être utilisées que pour intercepter des erreurs de programmation internes, ce qui signifie généralement que des paramètres incorrects ont été transmis.
#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>
int main(void)
{
int x = -1;
assert(x >= 0);
printf("x = %d\n", x);
return 0;
}
Sortie possible avec NDEBUG
non défini:
a.out: main.c:9: main: Assertion `x >= 0' failed.
Sortie possible avec NDEBUG
défini:
x = -1
Il est NDEBUG
définir NDEBUG
globalement afin de pouvoir facilement compiler votre code avec toutes les assertions NDEBUG
ou désactivées. Un moyen simple de le faire est de définir NDEBUG
comme une option pour le compilateur ou de le définir dans un en-tête de configuration partagé (par exemple, config.h
).
Affirmation statique
Les assertions statiques sont utilisées pour vérifier si une condition est vraie lorsque le code est compilé. Si ce n'est pas le cas, le compilateur doit émettre un message d'erreur et arrêter le processus de compilation.
Une assertion statique est une assertion qui est vérifiée au moment de la compilation, pas au moment de l'exécution. La condition doit être une expression constante et, si false, une erreur de compilation. Le premier argument, la condition qui est vérifiée, doit être une expression constante et le second un littéral de chaîne.
Contrairement à assert, _Static_assert
est un mot-clé. Une macro de commodité static_assert
est définie dans <assert.h>
.
#include <assert.h>
enum {N = 5};
_Static_assert(N == 5, "N does not equal 5");
static_assert(N > 10, "N is not greater than 10"); /* compiler error */
Avant C11, il n'y avait pas de support direct pour les assertions statiques. Cependant, en C99, les assertions statiques pouvaient être émulées avec des macros qui déclencheraient un échec de compilation si la condition de compilation était fausse. À la différence de _Static_assert
, le second paramètre doit être un nom de jeton approprié afin de pouvoir créer un nom de variable avec lui. Si l'assertion échoue, le nom de la variable apparaît dans l'erreur du compilateur, car cette variable a été utilisée dans une déclaration de tableau syntaxiquement incorrecte.
#define STATIC_MSG(msg, l) STATIC_MSG2(msg, l)
#define STATIC_MSG2(msg,l) on_line_##l##__##msg
#define STATIC_ASSERT(x, msg) extern char STATIC_MSG(msg, __LINE__) [(x)?1:-1]
enum { N = 5 };
STATIC_ASSERT(N == 5, N_must_equal_5);
STATIC_ASSERT(N > 5, N_must_be_greater_than_5); /* compile error */
Avant C99, vous ne pouviez pas déclarer des variables à des emplacements arbitraires dans un bloc, vous devriez donc être extrêmement prudent lorsque vous utilisez cette macro, en vous assurant qu'elle apparaît uniquement lorsqu'une déclaration de variable serait valide.
Affirmation de code inaccessible
Au cours du développement, lorsque certains chemins de code doivent être empêchés d'atteindre le flux de contrôle, vous pouvez utiliser assert(0)
pour indiquer qu'une telle condition est erronée:
switch (color) {
case COLOR_RED:
case COLOR_GREEN:
case COLOR_BLUE:
break;
default:
assert(0);
}
Chaque fois que l'argument de la macro assert()
évalué faux, la macro écrit des informations de diagnostic dans le flux d'erreur standard, puis abandonne le programme. Ces informations incluent le fichier et le numéro de ligne de l'instruction assert()
et peuvent être très utiles lors du débogage. Les assertions peuvent être désactivées en définissant la macro NDEBUG
.
Une autre façon de terminer un programme en cas d'erreur est d'utiliser les fonctions de bibliothèque standard exit
, quick_exit
ou abort
. exit
et quick_exit
prenez un argument qui peut être renvoyé à votre environnement. abort()
(et donc assert
) peut être une terminaison très grave de votre programme, et certains nettoyages qui seraient normalement effectués à la fin de l'exécution peuvent ne pas être exécutés.
Le principal avantage d’ assert()
est qu’il imprime automatiquement les informations de débogage. L'appel à abort()
présente l'avantage de ne pas pouvoir être désactivé comme une assertion, mais cela risque de ne pas entraîner l'affichage d'informations de débogage. Dans certaines situations, l'utilisation des deux concepts peut être bénéfique:
if (color == COLOR_RED || color == COLOR_GREEN) {
...
} else if (color == COLOR_BLUE) {
...
} else {
assert(0), abort();
}
Lorsque les assertions sont activées , l'appel assert()
imprimera les informations de débogage et mettra fin au programme. L'exécution n'atteint jamais l'appel abort()
. Lorsque les assertions sont désactivées , l'appel assert()
ne fait rien et abort()
est appelé. Cela garantit que le programme se termine toujours pour cette condition d'erreur; L'activation et la désactivation n'affectent que les effets de l'impression ou non de la sortie de débogage.
Vous ne devriez jamais laisser un tel assert
dans le code de production, car les informations de débogage ne sont pas utiles pour les utilisateurs finaux et parce que abort
est généralement une terminaison beaucoup trop sévère qui empêchent les gestionnaires de nettoyage qui sont installés pour la exit
ou quick_exit
à courir.
Assert Messages d'erreur
Un truc existe qui peut afficher un message d'erreur avec une assertion. Normalement, vous écrivez un code comme celui-ci
void f(void *p)
{
assert(p != NULL);
/* more code */
}
Si l'assertion a échoué, un message d'erreur ressemblerait à
L'assertion a échoué: p! = NULL, fichier main.c, ligne 5
Cependant, vous pouvez également utiliser un AND logique ( &&
) pour donner un message d'erreur.
void f(void *p)
{
assert(p != NULL && "function f: p cannot be NULL");
/* more code */
}
Maintenant, si l'assertion échoue, un message d'erreur indiquera quelque chose comme ceci
Echec de l'assertion: p! = NULL && "fonction f: p ne peut pas être NULL", fichier main.c, ligne 5
La raison pour laquelle cela fonctionne est qu'une chaîne littérale est toujours évaluée à zéro (true). L'ajout de && 1
à une expression booléenne n'a aucun effet. Ainsi, l'ajout de && "error message"
n'a aucun effet, sauf que le compilateur affichera l'intégralité de l'expression ayant échoué.