Почему (void) 0 нет операций в C и C++?

Я видел debug printfs в glibc, который внутренне определяется как (void) 0, если NDEBUG определен. Аналогично __noop для Visual C++ компилятор тоже есть. Первый работает на компиляторах GCC и VC++, а второй - только на VC++. Теперь мы все знаем, что оба вышеприведенных оператора будут рассматриваться как бездействие, и соответствующий код не будет сгенерирован; но здесь я сомневаюсь.

В случае __noop MSDN говорит, что это встроенная функция, предоставляемая компилятором. Подходит к (void) 0 ~ Почему это интерпретируется компиляторами как отсутствие операции? Это хитрое использование языка Си или стандарт говорит что-то об этом? Или даже это связано с реализацией компилятора?

5 ответов

Решение

(void)0 (+;) является допустимым, но выражением C++ "ничего не делает", вот и все. Это не переводит на no-op инструкция целевой архитектуры, это просто пустой оператор в качестве заполнителя всякий раз, когда язык ожидает завершения оператора (например, в качестве цели для метки перехода или в теле if пункт).

РЕДАКТИРОВАТЬ: (обновлено на основе комментария Криса Лутца)

Следует отметить, что при использовании в качестве макроса, скажем,

#define noop ((void)0)

(void) предотвращает его случайное использование в качестве значения, такого как

int x = noop;

Для вышеприведенного выражения компилятор справедливо пометит его как недопустимую операцию. GCC плюет error: void value not ignored as it ought to be и VC++ лает 'void' illegal with all types,

Любое выражение, которое не имеет побочных эффектов, может быть обработано компилятором как запретное действие, которое не должно генерировать какой-либо код для него (хотя это может быть). Так получилось, что приведение, а затем не использование результата приведения, легко для компилятора (и людей) увидеть как не имеющие побочных эффектов.

Я думаю, что вы говорите о glibc, а не о glib, и рассматриваемый макрос assert макрос:

В Глебце <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 имеет много смысла. Можете ли вы показать нам, где это делается?

Даже если это так, почему type бросает его в void? Кроме того, в случае #define dbgprintf (void) 0, когда он вызывается как dbgprintf("Hello World!"); -> (void) 0("Привет, мир!"); - что это значит? - legends2k

Макросы заменяют ваш код чем-то другим, поэтому, если вы #defined dbgprint (который принимает x) как

пустота (0)

тогда при замене X не произойдет перезапись X, поэтому dbgprintf("Helloworld") будет преобразован не в (void) 0("Hello world"), а в (void) 0; - не только имя макроса dbgprint заменяется на (void) 0, но и весь вызов dbgprintf("...")

В Windows я пытаюсь использовать такой код в Main.cpp:

#include <iostream>
#define TRACE ((void)0)
int main() {
  TRACE("joke");
  std::cout << "ok" << std::endl;
  return 0;
}

Затем я создаю исполняемый файл Release version с выходом Main.i. В файле Main.i макрос TRACE был заменен на:((void)0)("joke")и Visual Studio выдают предупреждение:"предупреждение C4353: используется нестандартное расширение: константа 0 в качестве выражения функции. Используйте взамен встроенную функцию" __noop "". Запустите exe-файл, консоль выведет "ок" символы. Поэтому я думаю, что все ясно: определение макроса TRACE[#define TRACE ((void)0)] недопустимо в соответствии с синтаксисом C++, но компилятор C++ в Visual Studio поддерживает это поведение как расширение компилятора. Итак, мой вывод: [#define TRACE ((void)0)] является недопустимым оператором C++, и вы в лучшем случае НЕ используйте его. Но [#define TRACE(x) ((void)0)] является юридическим утверждением. Это все.

Другие вопросы по тегам