2010-03-24 16 views
14

Der Standard besagt, dass die Dereferenzierung des Nullzeigers zu undefiniertem Verhalten führt. Aber was ist der Nullzeiger? Im folgenden Code, wie wir es nennen „der Null-Zeiger“:Welcher von diesen erstellt einen Nullzeiger?

struct X 
{ 
    static X* get() { return reinterpret_cast<X*>(1); } 
    void f() { } 
}; 

int main() 
{ 
    X* x = 0; 
    (*x).f(); // the null pointer? (1) 

    x = X::get(); 
    (*x).f(); // the null pointer? (2) 

    x = reinterpret_cast<X*>(X::get() - X::get()); 
    (*x).f(); // the null pointer? (3) 

    (*(X*)0).f(); // I think that this the only null pointer here (4) 
} 

Mein Gedanke ist, dass anstelle der Null-Zeiger-Dereferenzierung nur im letzten Fall nimmt. Habe ich recht? Gibt es einen Unterschied zwischen NULL-Zeigern zur Kompilierungszeit und Laufzeit gemäß C++ Standard?

+0

Das ist keine Hausaufgabe. Der C++ Standard sagt nicht viel über das Dereferenzieren der Nullzeiger aus. Ich will nur wissen. –

+0

Der erste Teil meiner Antwort hier spricht über Dereferenzierung von Null-Zeigern: http://StackOverflow.com/Questions/2474018/When-does-invoking-a-member-Function-on-Anull-Instance-Ergebnis-in- undefined-behav (ich liebe diese Frage.) – GManNickG

Antwort

12

Nur der erste und der letzte sind Nullzeiger. Die anderen sind Ergebnisse von reinterpret_cast und arbeiten daher mit implementierungsdefinierten Zeigerwerten. Ob das Verhalten für sie nicht definiert ist, hängt davon ab, ob sich ein Objekt an der Adresse befindet, an die Sie gecastet haben.

+0

Was ist der Unterschied zwischen der Dereferenzierung des Nullzeigers und einiger ungültiger Zeiger? –

+0

@Johannes: Sie sind Nullzeiger, aber verursachen sie einen Kernspeicherauszug? Anfangs dachte ich das, aber nach einigem Nachdenken bin ich mir nicht so sicher. Siehe meine Antwort oben. –

+2

@Scott Smith: Es ist die Implementierung definiert. Insbesondere auf Plattformen, die kein Konzept eines "Kerns" haben, d. H. Nichts außerhalb von UNIX. –

12

Ein ganzzahliger Konstantenausdruck, der zu 0 ausgewertet wird, ist als Nullzeiger gültig. Daher wird im ersten Fall auch ein Nullzeiger dereferenziert.

Ein Zeiger, der über eine arithmetische Berechnung auf 0 gesetzt wird, ist nicht unbedingt ein Nullzeiger. In den meisten Implementierungen wird es sich wie ein Null-Zeiger verhalten, aber dies wird vom Standard nicht garantiert.

+1

"Das Konvertieren eines ganzzahligen konstanten Ausdrucks (5.19) mit dem Wert Null ergibt immer einen Nullzeiger (4.10), aber das Konvertieren anderer Ausdrücke, die den Wert Null haben, muss keinen Nullzeiger ergeben" (5.2.10/5 Note 64). –

+1

@James Ahh! Also 0! = NULL für sehr große Werte von 0! – Earlz

+1

@Earlz: Aber 0! ist 1! – GManNickG

0
X* x = 0; 
(*x).f(); // the null pointer? (1) 

Ich denke, das als dereferenzieren qualifiziert, obwohl f() eigentlich nie verwendet die this Zeiger, und es gibt keine virtuellen Methoden in X. Mein Reflex war zu sagen, dass dies ein Unfall ist, aber jetzt, wo ich daran denke, bin ich mir nicht so sicher.

x = X::get(); 
(*x).f(); // the null pointer? (2) 

Wahrscheinlich ein ungültiger Zeiger. nicht sicher, ob es abstürzen wird (siehe oben für Argumentation).

x = reinterpret_cast<X*>(X::get() - X::get()); 
(*x).f(); // the null pointer? (3) 

Wird der Ausdruck X::get() - X::get() kompiliert? Ich hielt es nicht für legal, einen Zeiger von einem anderen Zeiger zu subtrahieren.

EDIT: D'oh! Natürlich ist es legal. Was habe ich gedacht? Klar, ich bin ein Kastanienbraun.

+0

In Bezug auf den Aufruf einer nicht statischen Funktion auf eine Null-Instanz: Es führt zu undefiniertem Verhalten. Also sind alle Wetten klar. Das heißt, in der Praxis, solange es keinen Bedarf für den "this" -Zeiger (keine virtuellen Funktionen, keine Mitglieder, etc.) gibt, wird es nicht abstürzen, da es nie benutzt wird. – GManNickG

+0

Subtrahieren von Zeigern ist sehr üblich - und es wird die Differenz zwischen den beiden Zeigern zurückgegeben. Da diese jedoch nicht im selben Array sind, haben Sie Recht - Ergebnis undefiniert. (aber es kompiliert wahrscheinlich) – pm100

+0

@Scott Smith, ich kompilierte das gerade in GNU C++ und führte ohne Fehler aus. Warum denken Sie, dass das Subtrahieren von Zeigern illegal ist? Mein erster Gedanke war, dass der Code in der OP-Frage in (2) abstürzt. –

7

C++ Norm (2003) 4,10

4,10 Pointer Conversions

1 Ein Nullzeiger Konstante ist ein Integralkonstante Ausdruck (5.19) rvalue ganzzahliger Typ, der auf Null auswertet. Eine Nullzeigerkonstante kann in einen Zeigertyp umgewandelt werden; Das Ergebnis ist der Nullzeigerwert von dieser Typ und unterscheidet sich von jeden anderen Wert des Zeigers auf Objekt oder Zeiger auf Funktionstyp. Zwei Null- Zeigerwerte des gleichen Typs sollen gleichen vergleichen. Die Umwandlung einer Nullzeiger-Konstanten in einen Zeiger auf cv-qualifizierter Typ ist eine einzige Umwandlung, und nicht die Sequenz einer Zeigerkonvertierung, gefolgt von einer Qualifizierungskonvertierung (4.4).

5.2.10 reinterpret gegossen

Note 64) Konvertieren einen Integralkonstante Ausdruck (5.19) mit dem Wert Null liefert immer einen Null-Zeiger (4.10), aber Umwandeln anderen Ausdrücke , die den Wert Null Notwendigkeit haben passieren nicht Gib einen Nullzeiger.

1) X* x = 0; (*x).f(); Ja. 0 ist ein ganzzahliger Konstantenausdruck und wird in die Nullzeigerkonstante konvertiert. Dann kann die Nullzeigerkonstante in den Nullzeigerwert umgewandelt werden.

2) x = X::get(); nein, siehe 64 Hinweis in 5.2.10

3) x = reinterpret_cast<X*>(X::get() - X::get()); nein, siehe Anmerkung 64 in 5.2.10

4) ((X) 0) .f() ; Ja. 0 (Integralkonstantenausdruck) -> die Nullzeigerkonstante -> der Nullzeigerwert.

+0

Für die Fußnote in 5.2.10, beachten Sie auch den Fehlerbericht in http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#463 –