Почему (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)] является юридическим выражением. Это все.