Quand devrions-nous utiliser asserts en C?

J'écris une fonction en C. Par style, quand est-il bon d'utiliser assert par rapport à renvoyer un code d'erreur. Disons que la fonction divise deux nombres. Devrais-je affirmer que le diviseur est différent de zéro ou devrais-je retourner un code d'erreur? Veuillez donner d'autres exemples, si vous le pouvez, pour clarifier la distinction.

30
c
demandé sur Bruce 2011-11-13 23:21:57

8 réponses

assert annule le processus, mais est transformé en no-op lorsque le programme est compilé avec -DNDEBUG, donc c'est un outil de débogage plutôt brut et rien de plus que cela. Vous devriez seulement utiliser assert pour vérifier les situations qui "ne peuvent pas arriver", par exemple qui violent les invariants ou les postconditions d'un algorithme, mais probablement Pas pour la validation des entrées (certainement pas dans les bibliothèques). Lorsque vous détectez une entrée non valide des clients, soyez amical et renvoyez un code d'erreur.

Un exemple d'utilisation de assert pourrait être: vous avez implémenté un algorithme de tri incroyablement intelligent et vous voulez vérifier s'il trie vraiment. Puisque la fonction de tri est supposée "fonctionner" et ne renvoie donc pas de valeur, vous ne pouvez pas ajouter de retours d'erreur sans changer L'API.

void sort(int *a, size_t n)
{
    recursive_super_duper_sort(a, 0, n);
    assert(is_sorted(a, n));
}

static bool is_sorted(int const *a, size_t n)
{
    for (size_t i=0; i<n-1; i++)
        if (a[i] > a[i+1])
            return false;

    return true;
}

À long terme, vous voudriez vraiment un cadre de test unitaire approprié pour ce genre de chose au lieu de assert, mais c'est utile comme outil de débogage temporaire.

36
répondu Fred Foo 2011-11-13 19:45:10

Un code d'erreur signale le comportement d'exécution. Une assertion est un outil de débogage qui permet au développeur d'affirmer que leurs hypothèses sur la logique du programme sont en effet vraies.

Ce sont deux choses complètement différentes avec des applications différentes.

Les codes d'erreur font partie de votre flux de programme normal. Les Assertions sont uniquement pour le débogage, et si une assertion est déclenchée, cela signifie que votre programme n'est pas écrit correctement.

17
répondu Kerrek SB 2018-08-21 17:11:59

En général, les affirmations sont pour le programmeur (c'est-à-dire vous) de trouver des erreurs de logique / programmation avant de libérer le programme aux utilisateurs réels. Les Asserts ne doivent pas être utilisés pour détecter les erreurs d'entrée d'exécution - utilisez des codes d'erreur pour ceux-ci.

14
répondu bobbymcr 2011-11-13 19:28:21

C'est vraiment une question de goût. Voici mon opinion.

La principale règle de base: un échec d'assertion est toujours un bug dans le programme.

Utilisez un assert pour vérifier les paramètres de fonction si vous vous attendez à ce que l'appelant s'assure que l'argument est correct et que vous voulez indiquer que tout autre comportement est un bogue dans l'appelant. Diviser par Zéro est, IMO, un très bon exemple.

Utilisez un code d'erreur si vous vous attendez à ce que l'appelant ne puisse pas s'assurer que l'argument est corrigez avant d'appeler. Par exemple, il pourrait être très coûteux de vérifier les arguments au préalable.

Jamais utiliser un assert pour vérifier la saisie de l'utilisateur.

8
répondu ibid 2011-11-13 19:28:56

La sagesse conventionnelle est d'utiliser assert () pour aider à déboguer votre code, pour vous avertir quand quelque chose "impossible", quelque chose qui ne doit pas arriver, est arrivé. Cet "avertissement" prend la forme de quitter votre programme.

J'ai entendu Jim Coplien (General c++ guru et Scrum trainer) préconiser de laisser vos assertions actives dans le code déployé. (Il a l'air fou, je sais...) C'était spécifiquement pour le code de serveur de haute fiabilité. La motivation était qu'il vaut mieux échouer, dur, et laisser un autre le nœud prend le relais, que de tolérer que votre serveur soit dans un État" impossible".

(et bien sûr, suivre les échecs, et les analyser. Cela signifie qu'il y a un bug ou une hypothèse incorrecte.)

8
répondu david van brink 2011-11-13 19:34:47

Tout d'abord, assert de l'en-tête <assert.h> peut être désactivé (par exemple en compilant avec gcc -DNDEBUG), et est parfois désactivé pour la version "production" d'un binaire.

Deuxièmement, comme indiqué par la page de manuel Linux,

   The  purpose  of  this macro is to help the programmer find bugs in his
   program.   The  message  "assertion  failed  in  file  foo.c,  function
   do_bar(), line 1287" is of no help at all to a user.

Donc assert ne devrait échouer que dans des situations de buggy. Dans des situations exceptionnelles ou d'erreur, vous devriez faire autre chose.

Certains outils (ou même des compilateurs) peuvent utiliser assert-ions pour par exemple optimiser votre code.

Dans votre exemple de fonction quotient, vous utiliserez assert si, dans l'ensemble de votre programme, vous êtes sûr que le diviseur doit être non nul (mais il pourrait être logique de nommer la fonction différemment, peut-être quotient_by_non_zero). Si vous considérez que cela pourrait arriver, faites-en un message fatal, une exception (c'est-à-dire longjmp en C), Un code d'erreur, etc.

4
répondu Basile Starynkevitch 2011-11-13 19:30:03

Puisque C ne prend pas en charge les exceptions, vous n'avez pas d'autre option que de renvoyer un code d'erreur. Un échec C assert() entraîne l'appel de abort() qui bombarde le processus. Ce n'est pas vraiment comparable à la gestion des erreurs standard.

Pour les opérations à virgule flottante, vous pouvez utiliser NaN pour signaler des conditions d'erreur. Pour les opérations entières, un code d'erreur est simplement votre seule option.

3
répondu David Heffernan 2011-11-13 19:24:47

Utiliser une assertion lorsque votre programme répond à une situation qui ne permet pas de continuer. Les Assertions sont un "contrat" et je les utilise comme un "contrat" avec OS et situation "danger en retard".

Afin d'émuler les exceptions, vous pouvez toujours utiliser Goto 'ERRORLABEL' et terminer clean après avoir exécuté une fonction de nettoyage.

2
répondu Michael Thuma 2011-11-13 19:30:25