Ich habe debug printfs in glibc gesehen, das intern als (void) 0
definiert ist, wenn NDEBUG definiert ist. Ebenso ist der __noop
für Visual C++ - Compiler vorhanden. Ersteres funktioniert sowohl auf GCC- als auch VC++ - Compilern, während letzteres nur auf VC++ basiert. Nun wissen wir alle, dass beide obigen Anweisungen als keine Operation behandelt werden und kein entsprechender Code generiert wird. Aber hier habe ich einen Zweifel.
Im Fall von __noop
gibt MSDN an, dass es sich um eine vom Compiler bereitgestellte intrinsische Funktion handelt. Kommen zu (void) 0
~ Warum wird es von den Compilern als keine Op interpretiert? Ist es eine knifflige Verwendung der C-Sprache oder sagt der Standard etwas dazu aus? Oder hat das auch etwas mit der Compiler-Implementierung zu tun?
(void)0
(+ ;
) ist ein gültiger C++ - Ausdruck, der nichts tut, das ist alles. Es übersetzt nicht in die Anweisung no-op
der Zielarchitektur, es ist nur eine leere Anweisung als Platzhalter, wenn die Sprache eine vollständige Anweisung erwartet (z. B. als Ziel für eine Sprungmarke oder im Text einer if
-Klausel).
EDIT: (aktualisiert auf Basis von Chris Lutzs Kommentar)
Es sei darauf hingewiesen, dass, wenn es als Makro verwendet wird, beispielsweise
#define noop ((void)0)
der (void)
verhindert, dass er versehentlich als Wert wie verwendet wird
int x = noop;
Für den obigen Ausdruck wird der Compiler ihn zu Recht als ungültige Operation kennzeichnen. GCC spuckt error: void value not ignored as it ought to be
und VC++ Barks 'void' illegal with all types
aus.
Alle Ausdrücke, die keine Nebenwirkungen haben, können vom Compiler als No-Op behandelt werden, der keinen Code dafür erzeugen muss (obwohl dies möglich ist). Es kommt daher vor, dass das Casting und die Nichtverwendung des Casting-Ergebnisses für den Compiler (und den Menschen) leicht zu sehen sind und keine Nebenwirkungen haben.
Ich denke, Sie sprechen von glibc , nicht glib , und das betreffende Makro ist das assert
-Makro:
In <assert.h>
von glibc ist NDEBUG
definiert, wobei assert
(kein Debugging) definiert ist:
#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
was im Grunde assert(whatever);
bedeutet, entspricht ((void)(0));
und tut nichts.
Aus dem C89-Standard (Abschnitt 4.2):
Der Header
<assert.h>
definiert das Makroassert
und verweist auf ein anderes Makro.NDEBUG
was nicht durch
<assert.h>
definiert ist. WennNDEBUG
als Makroname an der Stelle in der Quelldatei definiert ist, an der<assert.h>
enthalten ist, wird das Makroassert
einfach als definiert#define assert(ignore) ((void)0)
Ich halte es nicht für sinnvoll, ein Debug-Druckmakro so zu definieren, dass es gleich (void)0
ist. Kannst du uns zeigen, wo das gemacht wird?
Selbst wenn ja, warum sollte man es als ungültig erklären? Im Falle des #define dbgprintf (void) 0, wenn es wie dbgprintf ("Hello World!") Aufgerufen wird; -> (void) 0 ("Hallo Welt!"); - Was heißt das? - legends2k
Makros ersetzen Ihren Code durch etwas anderes, wenn Sie also #defined dbgprint (das x akzeptiert) als
ungültig (0)
dann wird kein X neu geschrieben, so dass dbgprintf ("Helloworld") nicht in (void) 0 ("Hello world"), sondern in (void) 0 umgewandelt wird. - Nicht nur der Makroname dbgprint wird durch (void) 0 ersetzt, sondern der gesamte Aufruf dbgprintf ("...").
Unter Windows versuche ich folgenden Code in Main.cpp:
#include <iostream>
#define TRACE ((void)0)
int main() {
TRACE("joke");
std::cout << "ok" << std::endl;
return 0;
}
Dann baue ich die Release-Version der exe-Datei mit der Main.i-Ausgabe. In der Main.i-Datei wurde das TRACE-Makro durch Folgendes ersetzt: ((void)0)("joke")
, und Visual Studio gibt eine Warnung aus: "warning C4353: Nicht standardmäßige Erweiterung verwendet: Konstante 0 als Funktionsausdruck. Verwenden Sie stattdessen die '__noop' - Funktion. Führen Sie die Exe-Datei aus, die Konsole druckt "ok" Zeichen aus. Ich denke, alles ist klar: Die Definition des Makros TRACE [#define TRACE ((void) 0)] ist gemäß der C++ - Syntax nicht zulässig, aber der C++ - Compiler von Visual Studio unterstützt dieses Verhalten als Compilererweiterung. Meine Schlussfolgerung lautet also: [#define TRACE ((void) 0)] ist eine illegale C++ - Anweisung, und Sie verwenden diese Einstellung NICHT. [#Define TRACE (x) ((void) 0)] ist jedoch eine rechtliche Aussage. Das ist alles.