2010-02-04 4 views
45

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

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

+0

Просто указывая '0;' как оператор, я не получаю предупреждений или ошибок, и я уверен, что он не будет выполнять какую-либо эффективную операцию и будет равен no op; Даже если это так, зачем вводить его в пустоту? Кроме того, в случае '#define dbgprintf (void) 0', когда он вызывается как' dbgprintf («Hello World!»); '->' (void) 0 («Hello World!»); '- что делает это значит? – legends2k

+3

это, вероятно, должно быть '#define dbgprintf (x) (void) 0;' хотя я нашел '#define dbgprintf (x)' вполне достаточным. Я думаю, что приведение к пустоте - это удаление любого возвращаемого значения, поэтому, если оно используется в контексте, которое требует значения (и не должно), оно приведет к ошибке/предупреждению, а не к молчанию. –

+0

Да, я не заметил, что #define игнорирует параметр (x) и просто выполняет '(void) 0'. Спасибо, что указали это :) – legends2k

ответ

59

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

EDIT: (обновляется на основе комментариев Криса Луца)

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

#define noop ((void)0) 

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

int x = noop; 

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

+42

Следует отметить, что в качестве макроса (например, '#define noop (void) 0')' (void) 'предотвращает его случайное использование в качестве значения (как в' int x = noop; '. –

+0

Благодарим Криса за разъяснение относительно того, какой тип применяется для создания пустоты, предотвращает случайное использование указанного noop exp в выражении типа присваивания, теперь я понял. – legends2k

+0

@ Крис: Положите это как ответ :). –

8

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

2

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

+0

Да, это glibc и не glib, спасибо за уведомление; скорректировал его в вопросе. – legends2k

+0

http://doc.ddart.net/msdn/header/include/assert.h.html - Это один из многих * assert.h * s, которые я видел с помощью '(void) 0'. – legends2k

+0

@ legendends2k: 'assert (expr);' не является макросом отладки печати. Мой ответ выше отвечает на ваш вопрос о 'assert()'. –

1

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

Макросов заменить код с чем-то еще, так что если вы с помощью #define DbgPrint (который принимает й), как

недействительного (0)

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

+0

Да, я понимаю, что :) – legends2k

0

В Windows я пытаюсь некоторый код, как это в main.cpp:

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

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