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