Почему assert-это макрос, а не функция?


мой преподаватель спросил меня, что в классе, и мне было интересно, почему это макрос, а не функция?

5 56

5 ответов:

простое объяснение будет заключаться в том, что стандарт требует assert чтобы быть макросом, если мы посмотрим на проект стандарта C99(насколько я могу судить разделы, одинаковы в проект стандарта C11 а также разделе)7.2Диагностика абзац 2 говорит:

макрос assert должен быть реализован как макрос, а не как фактическое функция. Если определение макроса подавлено в чтобы получить доступ к фактическая функция, поведение не определено.

почему это требует этого, обоснование приведено в обоснование международного стандарта-языки программирования-C - это:

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

что не очень информативно, но мы можем видеть из другие требования, почему. Возвращаясь к разделу 7.2 пункт 1 говорит:

[...]Если NDEBUG определяется как имя макроса в точке исходного файла где включен, макрос assert определяется просто как

#define assert(ignore) ((void)0)

макрос assert переопределяется в соответствии с текущим состоянием NDEBUG каждый раз, когда это включено.

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

и второе важное требование заключается в том, что необходимо использовать макрос __FILE__,__LINE__ и __func__, который рассматривается в разделе 7.2.1.1макрос assert он говорит:

[...] макрос assert записывает информацию о конкретном вызове это не удалось [...] последние являются соответственно значениями препроцессорные макросы __файл_ _ и __Line_ _ и идентификатора __func_ _) на стандартный поток ошибок в формате, определяемом реализацией.165) затем он вызывает функцию прерывания.

где сноска 165 говорит:

написанное сообщение может иметь вид:

Assertion failed: expression, function abc, file xyz, line nnn.

имея его в качестве макроса позволяет макросы __FILE__ etc... чтобы быть оцененным в правильном месте и, как указывает Иоахим, макрос позволяет ему вставлять оригинал выражение в сообщении он генерирует.

проект стандарта C++ требует, чтобы содержание cassert заголовок такой же, как assert.h заголовок из библиотеки Standrd C:

содержание такие же, как и стандартный заголовок C-библиотеки .

см. также: ISO C 7.2.

почему (void) 0?

зачем использовать (void)0 в отличие от некоторых других выражений, которые ничего не делают? Мы можем подняться с несколькими причинами, Во-первых, это то, как assert синопсис выглядит в разделе 7.2.1.1:

void assert(scalar expression);

и это говорит (выделено мной):

макрос assert помещает диагностические тесты в программы; it расширяется до пустого выражения.

выражение (void)0 согласуется с необходимостью в конечном итоге с Void выражение.

предполагая, что у нас не было этого требования, другие возможные выражения могут иметь нежелательные эффекты, такие как разрешение использования assert в режиме выпуска, который не будет разрешен в режиме отладки, например, с помощью plain 0 позволит нам использовать assert в задании и при правильном использовании, скорее всего, будет генерировать expression result unused предупреждение. Что касается использования составной оператор как следует из комментария, мы можем видеть из c многострочный макрос: do / while (0) vs scope block что у них есть нежелательные эффекты в некоторых случаях.

  1. это позволяет захватить файл (через __FILE__) и номер строки (через __LINE__)
  2. позволяет assert для замены допустимого выражения, которое ничего не делает (т. е. ((void)0)) при построении в режиме выпуска

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

#define NDEBUG 

в начале своего кода, перед включением <assert.h>.

таким образом, этот макрос предназначен для захвата программирования ошибки, а не ошибки пользователя или времени выполнения, поскольку он обычно отключается после выхода программы из фазы отладки.


создание его в качестве функции увеличит некоторые вызовы функций, и вы не можете контролировать все такие утверждения в режиме выпуска.

если вы используете функцию, то _FILE__,__LINE__ и __func__ даст значение кода этой функции assert. Не та вызывающая линия или линия вызывающей функции.

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

assert(is_identity(matrix * inverse))

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

или, может быть, утверждение относительно дешево, но оно содержится в очень коротком функцию, которая будет вызываться во внутреннем цикле. Или другие подобные обстоятельства.

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

почему assert является макросом, а не функцией?

потому что он должен быть скомпилирован в DEBUG режим и не должен компилироваться в релиз режим.