4 Stimmen

Casting NULL to void in C++

Ich bin auf eine verwirrende Notation gestoßen:

if(ptr != (void)(NULL)) {
    //some action
}

Also erweiterte es sich zu

if(ptr != (void)((void *)0)) {
    //some action
}

was zumindest seltsam erscheint. Gibt es wirklich einen rationalen Grund dafür oder ist es einfach sinnlos oder sogar falsch? Es hat jedoch problemlos kompiliert (auf Linux / gcc, ich erinnere mich nicht an die Version).

-BEARBEITEN-

Ich habe diesen Code heute überprüft, und hier sind neue Informationen: Zunächst einmal wurde das Casting in einem Makro verwendet und erweiterte sich zu

return (void)((void*)0);

innerhalb einer Funktion, die void zurückgibt. Der Code kompiliert mit gcc (GCC) 4.1.2 20080704 auf (Red Hat 4.1.2-50). Ist diese Aussage also gleichbedeutend mit

return void;

was gleichbedeutend mit

return;

oder steckt da mehr dahinter?

3voto

DarkDust Punkte 87647

Es sollte wahrscheinlich ein Cast zu einem void pointer sein, was dasselbe wie einfach "Pointer" bedeutet (d. h. ein Zeiger auf jeden Typ):

if (ptr != (void *)(NULL)) {
    ...
}

Obwohl der Cast unnötig ist:

if (ptr != NULL) {
    ...
}

Ihr Code kompiliert nicht einmal auf meinem System (Mac mit GCC 4.2.1), die Fehlermeldung, die GCC meldet, lautet void value not ignored as it ought to be. Das ist zu erwarten, da void bedeutet kein Wert/Typ überhaupt, und Sie können das nicht mit etwas anderem vergleichen.

Das besagt der C99-Standard:

§6.3.2.2 void

1 Der (nicht existierende) Wert eines void-Ausdrucks (eines Ausdrucks mit dem Typ void) darf in keiner Weise verwendet werden, und implizite oder explizite Umwandlungen (außer zu void) dürfen auf solch einem Ausdruck nicht angewendet werden. Wird ein Ausdruck eines anderen Typs als ein void-Ausdruck bewertet, wird sein Wert oder Bezeichner verworfen. (Ein void-Ausdruck wird für seine Seiteneffekte bewertet.)

§6.5.9 Gleichheitsoperatoren

Einschränkungen

2 Eine der folgenden Bedingungen muss erfüllt sein:

  • beide Operanden haben arithmetischen Typ;
  • beide Operanden sind Zeiger auf qualifizierte oder unqualifizierte Versionen kompatibler Typen;
  • ein Operand ist ein Zeiger auf einen Objekt oder unvollständigen Typ und der andere ist ein Zeiger auf eine qualifizierte oder unqualifizierte Version von void; oder
  • ein Operand ist ein Zeiger und der andere ist eine null pointer-Konstante.

§6.3.2.2 verbietet bereits die Verwendung von void, und die Einschränkungen in §6.5.9 würden ebenfalls verletzt werden.

Wenn ein Compiler ptr != (void)(NULL) zulassen würde, würde er gegen den C99-Standard verstoßen. Es könnte einfach ein Fehler oder ein Fehlverhalten einer älteren GCC-Version sein.

Ich habe nur einen Entwurf für C89 gefunden, und hier ist die void-Sektion fast identisch mit der in C99. Aber die Sektion über die Gleichheitsoperatoren lässt mich ratlos zurück. Es scheint, als ob der Vergleich mit void erlaubt ist, aber dies könnte ein Fehler im Entwurf oder ein Problem mit der Formulierung sein:

§3.2.2.2 void

Der (nicht existierende) Wert eines void-Ausdrucks (eines Ausdrucks, der den Typ void hat) darf in keiner Weise verwendet werden, und implizite oder explizite Umwandlungen (außer zu void) dürfen auf solch einem Ausdruck nicht angewendet werden. Wird ein Ausdruck eines anderen Typs in einem Kontext ausgewertet, in dem ein void-Ausdruck erforderlich ist, wird sein Wert oder Bezeichner verworfen. (Ein void-Ausdruck wird für seine Seiteneffekte bewertet.)

§3.3.9 Gleichheitsoperatoren

...

Einschränkungen

Es muss eine der folgenden Bedingungen erfüllt sein:

  • beide Operanden haben arithmetischen Typ;
  • beide Operanden sind Zeiger auf qualifizierte oder unqualifizierte Versionen kompatibler Typen;
  • ein Operand ist ein Zeiger auf ein Objekt oder unvollständigen Typ und der andere ist eine qualifizierte oder unqualifizierte Version von void; oder
  • ein Operand ist ein Zeiger und der andere ist eine null pointer-Konstante.

1voto

rubikonx9 Punkte 1393

Ich habe die Assembly für diesen Fall überprüft.

Mit einer solchen Funktion:

void x() {
    return (void)(NULL);
}

Das Ausführen des Präprozessors als: gcc x.c -E -o x.E -Wall -Wextra ergibt:

void x() {
    return (void)(((void *)0));
}

Was ziemlich bizarr erscheint. Auf jeden Fall ist die Assembly nach der Kompilierung mit gcc x.c -o x -Wall -Wextra (von objdump -d x):

0804841d :
 804841d:   55                      push   %ebp
 804841e:   89 e5                   mov    %esp,%ebp
 8048420:   90                      nop
 8048421:   5d                      pop    %ebp
 8048422:   c3                      ret    

Es ist genau dasselbe wie die Assembly für:

void x() {
    return;
}

Also sind diese beiden Ausdrücke äquivalent.

Ich verwende gcc Version 4.8.2 (2013), für 64-Bit-Linux, also handelt es sich nicht um einen Fehler einer alten gcc-Version.

Das Hinzufügen von -pedantic zu gcc führt jedoch zu einer Warnung:

Warnung: ISO C verbietet 'return' mit Ausdruck, in Funktion die void zurückgibt [-Wpedantic]

return (void)(NULL);

Dies geschieht für alle Dialekte: c89, c99, gnu89, gnu99.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X