Подтвердить что ты не робот

Что означает утверждение (0)?

У меня был такой вопрос на одном из моих экзаменов, и я все еще не слишком уверен, как ответить на него. Я понимаю, что утверждения - это способы тестирования вашей программы, однако я не уверен, что проверяет assert(0). Это вопрос с подвохом? Он всегда будет терпеть неудачу, но я не понимаю, почему. Что он проверяет?

Любое объяснение было бы замечательным, спасибо.

4b9b3361

Ответ 1

Стандарт С++ отсылает определение assert к стандарту C.

C99 §7.2/2:

" Макрос assert помещает диагностические тесты в программы, он расширяется до выражения void. Когда он выполняется, если выражение (которое должно иметь скалярный тип) является ложным (то есть, сравнивается с 0), макрос утверждения записывает информацию об отказе конкретного вызова (включая текст аргумента, имя исходного файла, номер строки источника и имя закрывающей функции - последние являются соответственно значения макросов предварительной обработки __FILE__ и __LINE__ и идентификатора __func__) в стандартном файле ошибки в определенном для реализации формате. Затем он вызывает функцию abort.


В assert(0) 0 интерпретируется как false, поэтому это утверждение всегда терпит неудачу или огонь, когда проверка утверждения включена.

Таким образом, он утверждает, что

"Выполнение никогда не достигнет этой точки."

На практике может быть сложно заставить компиляторы заткнуться о выполнении, достигнутом или не достигшем заданной точки. Часто компилятор сначала жалуется на выполнение, возможно, доходящее до конца функции, не возвращая значения. Добавление assert(0) должно идеально решить эту проблему, но тогда компилятор может жаловаться на assert или не признать, что он говорит, что вы уже хорошо осведомлены о том, о чем он пытается предупредить.

Одна возможная мера (1) должна также генерировать исключение в этой точке:

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

Разумеется, что двойной удар можно определить как макрос более высокого уровня. Что касается типа исключения, вы можете сохранить его как не std::exception, потому что это не исключение, предназначенное для того, чтобы быть пойманным обычным catch в любом месте. Или, если вы доверяете стандартной иерархии исключений (для меня это не имеет смысла, но), вы можете использовать std::logic_error.


Чтобы отключить проверку подтверждения assert, вы можете определить символ NDEBUG, прежде чем включать <assert.h>.

Этот заголовок имеет специальную поддержку, поэтому вы можете включать его несколько раз, с или без NDEBUG.

С++ 11 §17.6.2.2/2:

" Единица перевода может включать заголовки библиотек в любом порядке (раздел 2). Каждый из них может быть включен больше, чем один раз, без какого-либо эффекта, отличного от того, чтобы быть включенным ровно один раз, за ​​исключением того, что эффект включения либо <cassert>, либо <assert.h> зависит каждый раз от определения lexically current NDEBUG.

Разумное определение двойного удара, о котором говорилось выше, также может зависеть от NDEBUG, не включающего охрану, например

Файл 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

Отказ от ответственности: хотя я кодировал это несколько раз в начале 2000-х годов, я приготовил код выше только для этого ответа, и, хотя я тестировал его с помощью g++, он может не быть идеальным.


(1) См. ответ Базиля Старинкевича для обсуждения другой возможности, g++ - специфического внутреннего __builtin_unreachable.

Ответ 2

Он всегда будет терпеть неудачу. Это в значительной степени. Он будет терпеть неудачу всегда по той же причине, что "assert (x == 5)" будет успешным всякий раз, когда x = 5.

Если вы запрашиваете приложение, вы должны поместить его в блоки кода, которые действительно не должны произойти.

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

Ответ 3

Да, он всегда будет терпеть неудачу.

assert(0) или assert(false) обычно используется для отметки недоступного кода, поэтому в режиме отладки выводится диагностическое сообщение, и программа прерывается, когда фактически недостижимое фактически достигается, что ясный сигнал о том, что программа не делает то, что мы считаем.

Ответ 4

В дополнение к другим ответам (особенно this), если вы используете недавний GCC (или Clang), вы можете рассмотреть возможность использования GCC builtin, особенно __builtin_unreachable() вместо assert(0).

Есть некоторые отличия: во-первых, assert можно отключить с помощью -DNDEBUG. И __builtin_unreachable изменит способ оптимизации вашего компилятора.

Конечно, некоторые компиляторы не знают о __builtin_unreachable.

И вы также можете рассмотреть возможность вызова некоторой функции [[noreturn]] С++ (в С++ 11 или выше) - или __attribute__((noreturn)) для GCC, например abort()

BTW, assert(0) не совсем похоже на то, что выбрасывает какое-то исключение (поскольку исключения могут быть пойманы)