Adloun

Une assertion qui change le programme

Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 4 — Discipline de programmation, validation et test

Énoncé

Ce programme n'affiche pas la même chose selon qu'on le compile avec ou sans -DNDEBUG. Pourquoi, et quelle règle en tirer ?


int appels = 0;
int suivant(void) { appels = appels + 1; return appels; }

int main(void) {
    assert(suivant() > 0);
    printf("appels = %d\n", appels);
    printf("suivant() = %d\n", suivant());
    return 0;
}

Corrigé


gcc -Wall -Wextra -std=c99            : appels = 1   suivant() = 2
gcc -Wall -Wextra -std=c99 -DNDEBUG   : appels = 0   suivant() = 1

Ce qui se passe. assert est une macro, et la définition standard la réduit à rien quand NDEBUG est défini. Ce n'est pas seulement le test qui disparaît : c'est toute l'expression, avec ses effets. Ici, l'appel à suivant() n'a jamais lieu, et le compteur reste à zéro.

La règle : une assertion ne doit contenir aucun effet de bord. Pas d'appel qui modifie un état, pas d'affectation, pas d'incrémentation, pas de lecture sur un flux. On écrit :


int v = suivant();      /* l'effet a lieu, toujours */
assert(v > 0);          /* la verification, elle, peut disparaitre */

Et le corollaire, plus important encore. Une assertion vérifie une propriété ; elle ne réalise rien. Sur le même programme :


int racine_entiere(int n) { assert(n >= 0); ... }

sans NDEBUG : Assertion failed: (n >= 0), function racine_entiere,
              file afail.c, line 3.        code de sortie 134
avec NDEBUG : 0                            code de sortie 0

La version livrée ne signale plus rien et rend pour . C'est cohérent avec ce qu'une assertion est — un filet pour le développement, qui suppose la précondition tenue — et c'est incompatible avec l'idée de s'en servir pour valider une entrée. Une donnée qui vient de l'extérieur se teste par un if, qui, lui, ne disparaît jamais.

iRemarqueEn OCaml, l'assertion résiste mieux

assert y est une construction du langage, pas une macro : elle lève Assert_failure et n'est retirée que par l'option explicite -noassert. Le piège des effets de bord y est donc moins vicieux — mais la règle reste la même, car -noassert existe.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.