Почему assert-это макрос, а не функция?
мой преподаватель спросил меня, что в классе, и мне было интересно, почему это макрос, а не функция?
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
в режиме выпуска, который не будет разрешен в режиме отладки, например, с помощью plain0
позволит нам использоватьassert
в задании и при правильном использовании, скорее всего, будет генерироватьexpression result unused
предупреждение. Что касается использования составной оператор как следует из комментария, мы можем видеть из c многострочный макрос: do / while (0) vs scope block что у них есть нежелательные эффекты в некоторых случаях.
- это позволяет захватить файл (через
__FILE__
) и номер строки (через__LINE__
)- позволяет
assert
для замены допустимого выражения, которое ничего не делает (т. е.((void)0)
) при построении в режиме выпуска
этот макрос отключается, если на момент включения уже определен макрос с именем NDEBUG. Это позволяет кодеру включать столько вызовов assert, сколько необходимо в исходном коде при отладке программы, а затем отключить все из них для производственной версии, просто включив строку типа:
#define NDEBUG
в начале своего кода, перед включением
<assert.h>
.таким образом, этот макрос предназначен для захвата программирования ошибки, а не ошибки пользователя или времени выполнения, поскольку он обычно отключается после выхода программы из фазы отладки.
создание его в качестве функции увеличит некоторые вызовы функций, и вы не можете контролировать все такие утверждения в режиме выпуска.
если вы используете функцию, то
_FILE__
,__LINE__
и__func__
даст значение кода этой функции assert. Не та вызывающая линия или линия вызывающей функции.
некоторые утверждения могут быть дорогими назвать. Вы только что написали высокоэффективную процедуру инверсии матрицы, и вы добавляете проверку здравомыслия
assert(is_identity(matrix * inverse))
до конца. Ну, ваши матрицы довольно большие, и если
assert
это функция, это займет много времени, чтобы сделать вычисление, прежде чем передать его в assert. Время, которое вы действительно не хотите тратить, если вы не занимаетесь отладкой.или, может быть, утверждение относительно дешево, но оно содержится в очень коротком функцию, которая будет вызываться во внутреннем цикле. Или другие подобные обстоятельства.
делая
assert
макрос вместо этого, вы можете полностью исключить расчет для утверждения выключены.