web-dev-qa-db-fra.com

Que signifie assert (0)?

J'ai eu une question comme celle-ci lors d'un de mes examens et je ne sais toujours pas trop comment y répondre. Je comprends que les assertions sont des moyens de tester votre programme, mais je ne suis pas trop sûr de ce que assert(0) vérifie. Est-ce une question piège? Cela échouera toujours, mais je ne comprends pas pourquoi. Que vérifie-t-il?

Toute explication serait formidable, merci.

26
Smith Western

Cela échouera toujours. C'est à peu près ça. Il échouera toujours pour la même raison que "assert (x == 5)" réussira chaque fois que x = 5.

Si vous demandez un application alors vous le mettriez dans des blocs de code qui ne devraient vraiment pas se produire.

switch(suit) {
  case CLUB:
  case DIAMOND:
  case HEART:
  case SPADE:
  // ...
  default:
    assert(0);
 }
12
djechlin

Le standard C++ reporte la définition de assert au standard C.

La macro assert place les tests de diagnostic dans les programmes; il se développe en une expression vide. Lorsqu'elle est exécutée, si expression (qui doit avoir un type scalaire) est faux (c'est-à-dire qu'il est égal à 0), la macro d'assertion écrit des informations sur l'appel particulier qui a échoué (y compris le texte de l'argument, le nom du fichier source, le numéro de ligne source et le nom de la fonction englobante - ces derniers sont respectivement les valeurs des macros de prétraitement __FILE__ et __LINE__ et de l'identifiant __func__) sur le fichier d'erreur standard dans un format défini par l'implémentation. Il appelle ensuite la fonction abort.


Dans assert(0) le 0 Est interprété comme false, donc cette assertion échouera toujours, ou se déclenchera , lorsque la vérification des assertions est activée.

Il affirme ainsi que

"L'exécution n'atteindra jamais ce point."

En pratique, il peut être difficile de faire taire les compilateurs lorsque l'exécution atteint ou n'atteint pas un point donné. Souvent, le compilateur se plaindra d'abord que l'exécution peut atteindre la fin d'une fonction sans renvoyer de valeur. L'ajout d'une assert(0) devrait idéalement résoudre ce problème, mais le compilateur peut alors se plaindre du assert, ou ne pas reconnaître qu'il dit que vous êtes déjà bien conscient de ce qu'il essaie d'avertir. .

Un (1)la mesure possible est alors de lever également une exception à ce point:

auto foo( int x )
    -> int
{
    if( x == 1 ) { return 42; }
    assert( 0 ); throw 0;       // Should never get here!
}

Bien sûr, ce double coup dur peut être défini comme une macro de niveau supérieur. En ce qui concerne le type d'exception, vous souhaiterez peut-être le conserver comme un std::exception, Car il ne s'agit pas d'une exception destinée à être interceptée par un catch ordinaire n'importe où. Ou, si vous faites confiance à la hiérarchie d'exceptions standard (cela n'a pas de sens pour moi, mais) vous pouvez utiliser un std::logic_error.


Pour désactiver la vérification d'assertion assert, vous pouvez définir le symbole NDEBUG avant d'inclure <assert.h>.

Cet en-tête a un support spécial pour que vous puissiez l'inclure plusieurs fois, avec ou sans NDEBUG défini.

Une unité de traduction peut inclure des en-têtes de bibliothèque dans n'importe quel ordre (article 2). Chacun peut être inclus plus d'une fois, sans effet différent d'être inclus exactement une fois, sauf que l'effet d'inclure soit <cassert> Soit <assert.h> Dépend à chaque fois de la définition lexicale actuelle de NDEBUG.

Une définition raisonnable du double coup dur discuté ci-dessus peut également dépendre de NDEBUG sans garde d'inclusion, par ex.

assert_should_never_get_here.hpp
#include <stdexcept>        // std::logic_error
#include <assert.h>

#undef ASSERT_SHOULD_NEVER_GET_HERE
#ifdef NDEBUG
#   define ASSERT_SHOULD_NEVER_GET_HERE() \
        throw std::logic_error( "Reached a supposed unreachable point" )
#else
#   define ASSERT_SHOULD_NEVER_GET_HERE() \
        do{ \
            assert( "Reached a supposed unreachable point" && 0 ); \
            throw 0; \
        } while( 0 )
#endif

Avertissement: Bien que j'aie codé cela plusieurs fois au début des années 2000, j'ai préparé le code ci-dessus juste pour cette réponse, et même si je l'ai testé avec g ++, il n'est pas nécessairement parfait.


(1) Voir réponse de Basile Starynkevitch pour une discussion sur une autre possibilité, l'intrinsèque spécifique à g ++ __builtin_unreachable.

30

Oui, cela échouera toujours.

assert(0) ou assert(false) est généralement utilisé pour marquer code inaccessible, de sorte qu'en mode débogage, un message de diagnostic est émis et le programme est abandonné lorsque le soi-disant inaccessible est effectivement atteint, ce qui indique clairement que le programme ne fait pas ce que nous pensons être.

9
emlai

En ajout à d'autres réponses (notamment this one), si vous utilisez une récente GCC (ou Clang ), vous pourriez envisager d'utiliser un peu GCC builtin , notamment __builtin_unreachable() au lieu de assert(0).

Il existe quelques différences: premièrement, assert peut être désactivé avec -DNDEBUG. Et __builtin_unreachable Changera la façon dont le compilateur optimise votre code.

Bien sûr, certains compilateurs ne connaissent pas __builtin_unreachable.

Et vous pouvez également envisager d'appeler une fonction C++ [[noreturn]] (En C++ 11 ou mieux) - ou __attribute__((noreturn)) pour GCC , comme abort()

BTW, assert(0) n'est pas exactement comme lancer une exception (car des exceptions peuvent être interceptées)

3