webentwicklung-frage-antwort-db.com.de

Warum ist (ungültig) keine Operation in C und C++?

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?

45
legends2k

(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.

61

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.

8
anon

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 Makro assert und verweist auf ein anderes Makro.

NDEBUG

was nicht durch <assert.h> definiert ist. Wenn NDEBUG als Makroname an der Stelle in der Quelldatei definiert ist, an der <assert.h> enthalten ist, wird das Makro assert 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?

2
Alok Singhal

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 ("...").

1

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.

0
baojing.zhou