Почему 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макрос вместо этого, вы можете полностью исключить расчет для утверждения выключены.