Почему (void) 0 a нет операции в C и С++?
Я видел debug printfs в glibc, который внутренне определен как (void) 0
, если определено NDEBUG. Аналогично __noop
для компилятора Visual С++. Первый работает как на компиляторах GCC, так и на VС++, а последний - только на VС++. Теперь мы все знаем, что оба вышеуказанных утверждения будут рассматриваться как никакие операции, и никакой соответствующий код не будет сгенерирован; но здесь, где я сомневаюсь.
В случае __noop
MSDN говорит, что это внутренняя функция, предоставляемая компилятором. Переход к (void) 0
~ Почему он интерпретируется компиляторами как нет op? Является ли это сложным использованием языка C, или стандарт говорит что-то об этом? Или даже это как-то связано с реализацией компилятора?
Ответы
Ответ 1
(void)0
(+ ;
) - это действительное, но "не-ничего" выражение С++, что все. Он не преобразуется в инструкцию no-op
целевой архитектуры, это просто пустой оператор в качестве заполнителя всякий раз, когда язык ожидает полного утверждения (например, в качестве цели для метки перехода или в теле предложения if
).
EDIT: (обновлено на основе комментария Криса Лутца)
Следует отметить, что при использовании в качестве макроса, скажем
#define noop ((void)0)
(void)
предотвращает случайное использование в качестве значения, например
int x = noop;
В приведенном выше выражении компилятор правильно помечает это как недопустимую операцию. GCC плюет error: void value not ignored as it ought to be
и VС++ barks 'void' illegal with all types
.
Ответ 2
Любое выражение, не имеющее каких-либо побочных эффектов, может быть обработано компилятором как no-op, которое не должно генерировать для него какой-либо код (хотя это может быть). Так получилось, что литье, а затем не использование результата литья, легко компилятору (и людям) увидеть как не имеющее побочных эффектов.
Ответ 3
Я думаю, вы говорите о glibc, а не glib, а макросом является макрос assert
:
В glibc <assert.h>
, с NDEBUG
(без отладки), assert
определяется как:
#ifdef NDEBUG
#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif
# define assert(expr) (__ASSERT_VOID_CAST (0))
#else
/* more code */
#endif
который в основном означает assert(whatever);
, эквивалентен ((void)(0));
и ничего не делает.
Из стандарта C89 (раздел 4.2):
Заголовок <assert.h>
определяет макрос assert
и ссылается на другой макрос,
NDEBUG
который не определен <assert.h>
. Если NDEBUG
определяется как имя макроса в точке исходного файла, где <assert.h>
включен, макрос assert
определяется просто как
#define assert(ignore) ((void)0)
Я не думаю, что определение макроса отладочной печати равным (void)0
имеет смысл. Можете ли вы показать нам, где это делается?
Ответ 4
Даже если так, зачем вводить его в пустоту? Кроме того, в случае #define dbgprintf (void) 0, когда он называется как dbgprintf ( "Hello World!" ); → (void) 0 ( "Hello World!" ); - что это значит? - legendends2k
Макросы заменяют ваш код чем-то другим, поэтому, если вы #defined dbgprint (который принимает x) как
void (0)
тогда замена не заменит X, поэтому dbgprintf ( "Helloworld" ) не будет преобразован в (void) 0 ( "Hello world" ), но to (void) 0; - не только имя макроса dbgprint заменяется на (void) 0, но весь вызов dbgprintf ( "..." )
Ответ 5
В Windows я пытаюсь использовать такой код в Main.cpp:
#include <iostream>
#define TRACE ((void)0)
int main() {
TRACE("joke");
std::cout << "ok" << std::endl;
return 0;
}
Затем я создаю exe файл Release version с выходом Main.i. В файле Main.i макрос TRACE был заменен на: ((void)0)("joke")
, а визуальная студия выдает предупреждение: "предупреждение C4353: нестандартное расширение используется: константа 0 как выражение функции. Вместо этого используйте функцию" __noop "intrinsic вместо". Запустите exe файл, консоль распечатает символы "ok". Поэтому я думаю, что все понятно: определение макроса TRACE [#define TRACE ((void) 0)] является незаконным в соответствии с синтаксисом С++, но С++-компилятор visual studio поддерживает это поведение как расширение компилятора. Поэтому мой вывод: [#define TRACE ((void) 0)] является незаконным оператором С++, и вы в лучшем случае НЕ используйте это. Но [#define TRACE (x) ((void) 0)] является юридическим выражением. Это все.