Почему утверждается макрос, а не функция?

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

Ответ 1

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

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

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

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

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

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

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

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

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

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

[...] макрос подтверждения записывает информацию о конкретном вызове которые потерпели неудачу [...], последние являются соответственно значениями препроцессорные макросы __FILE_ _ и __LINE_ _ и идентификатора __func_ _) в стандартном потоке ошибок в определенном для реализации формате. 165) Затем он вызывает функцию прерывания.

где примечание 165 гласит:

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

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

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

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

Содержимое совпадает с заголовком библиотеки Standard C.

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

Почему (void) 0?

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

void assert(scalar expression);

и он говорит (внимание мое):

Макрос утверждений ставит диагностические тесты в программы; он расширяется до выражения void.

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

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

Ответ 2

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

Ответ 3

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

#define NDEBUG 

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

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


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

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

Ответ 4

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

assert(is_identity(matrix * inverse))

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

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

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

Ответ 5

Почему утверждается макрос, а не функция?

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