Wann kommt den Aufruf einer member-Funktion auf eine null-Instanz nicht definiertes Verhalten zur Folge?
Betrachten Sie den folgenden code:
#include <iostream>
struct foo
{
//(a):
void bar() { std::cout << "gman was here" << std::endl; }
//(b):
void baz() { x = 5; }
int x;
};
int main()
{
foo* f = 0;
f->bar(); //(a)
f->baz(); //(b)
}
Erwarten wir (b)
zum Absturz bringen, da es kein entsprechendes Mitglied x
für den null-Zeiger. In der Praxis (a)
nicht Abstürzen, weil die this
Zeiger nie benutzt.
Weil (b)
dereferenziert die this
Zeiger ((*this).x = 5;
), und this
null ist, wird das Programm in undefiniertem Verhalten, wie die Dereferenzierung von null ist immer gesagt zu undefiniert Verhalten.
Tut (a)
nicht definiertes Verhalten zur Folge? Was ist, wenn beide Funktionen (und x
) sind statisch?
Wenn beide Funktionen static, wie könnte x bezeichnet werden innerhalb von baz? (x ist eine nicht-statische member-variable)
Vorgeben
Sicherlich, aber für den Fall (a) es funktioniert auf die gleiche in allen Fällen, ich.e, wird die Funktion automatisch aufgerufen. Allerdings, und ersetzen Sie den Wert des Zeigers, der von 0 bis 1 (z.B. durch reinterpret_cast), ist es fast ausnahmslos immer abstürzt. Auch der Wert Zuweisung 0 und somit NULL, wie im Fall a stellt etwas besonderes an den compiler ? Warum muss es immer Abstürzen, mit einem anderen Wert zugewiesen?
Interessant: Kommt die nächste revision von C++, es darf nicht mehr dereferenzieren von Zeigern an alle. Wir sind jetzt führen Sie indirection über Zeiger. Um mehr zu erfahren, führen Sie bitte Umweg über diesen link: N3362
Aufruf einer member-Funktion einen null-Zeiger ist immer undefinierten Verhalten. Einfach durch einen Blick auf deinen code, ich kann bereits spüren, das Undefinierte Verhalten langsam kriecht meinen Hals!
Vorgeben
x
gemacht wurde, auch statisch. 🙂Sicherlich, aber für den Fall (a) es funktioniert auf die gleiche in allen Fällen, ich.e, wird die Funktion automatisch aufgerufen. Allerdings, und ersetzen Sie den Wert des Zeigers, der von 0 bis 1 (z.B. durch reinterpret_cast), ist es fast ausnahmslos immer abstürzt. Auch der Wert Zuweisung 0 und somit NULL, wie im Fall a stellt etwas besonderes an den compiler ? Warum muss es immer Abstürzen, mit einem anderen Wert zugewiesen?
Interessant: Kommt die nächste revision von C++, es darf nicht mehr dereferenzieren von Zeigern an alle. Wir sind jetzt führen Sie indirection über Zeiger. Um mehr zu erfahren, führen Sie bitte Umweg über diesen link: N3362
Aufruf einer member-Funktion einen null-Zeiger ist immer undefinierten Verhalten. Einfach durch einen Blick auf deinen code, ich kann bereits spüren, das Undefinierte Verhalten langsam kriecht meinen Hals!
InformationsquelleAutor GManNickG | 2010-03-18
Du musst angemeldet sein, um einen Kommentar abzugeben.
Beide
(a)
und(b)
nicht definiertes Verhalten zur Folge. Es ist immer Undefiniertes Verhalten zum Aufruf einer member-Funktion durch einen null-Zeiger. Wenn die Funktion statisch ist, ist es technisch undefined als gut, aber es gibt einige Streit.Das erste, was zu verstehen ist, warum es zu undefiniertem Verhalten zu dereference einen null-Zeiger. In C++03, es ist eigentlich ein bisschen von Ambiguität.
Obwohl "dereferenzieren eines null-Zeigers Ergebnisse zu undefiniertem Verhalten" erwähnt wird in den Anmerkungen in beiden §1.9/4 und §8.3.2/4, ist es nie explizit angegeben ist. (Notes sind nicht-normative.)
Allerdings kann man versuchen, ableiten, die es aus §3.10/2:
Beim dereferenzieren, das Ergebnis ist ein lvalue. Ein null-Zeiger nicht auf ein Objekt verweisen, also wenn wir von lvalue wir haben Undefiniertes Verhalten. Das problem ist, dass der Vorherige Satz ist nie angegeben, was also bedeutet es für die "Verwendung" der lvalue? Nur noch erzeugen Sie überhaupt, oder, um es in den mehr formalen Sinn von führen lvalue-rvalue-Konvertierung?
Unabhängig davon, ist es definitiv nicht konvertiert werden, um einen rvalue (§4.1/1):
Hier ist es definitiv nicht definiertes Verhalten.
Die Mehrdeutigkeit kommt von, ob oder nicht es ist Undefiniertes Verhalten, Rücksicht aber nicht verwenden den Wert mit einem ungültigen Zeiger (das ist, um ein lvalue, aber nicht konvertieren es in ein rvalue). Wenn nicht, dann
int *i = 0; *i; &(*i);
ist gut definiert. Dies ist ein aktiv Problem.Also wir haben eine strikte "dereference einen null-Zeiger, bekommen Undefiniertes Verhalten" anzeigen " und " schwach "verwenden Sie einen null-Zeiger dereferenziert, bekommen Undefiniertes Verhalten" anzeigen.
Betrachten wir nun die Frage stellen.
Ja,
(a)
Ergebnisse zu undefiniertem Verhalten. In der Tat, wennthis
null ist, dann unabhängig vom Inhalt der Funktion, ist das Ergebnis undefiniert.Dies folgt aus §5.2.5/3:
*(E1)
führt zu undefiniertem Verhalten mit einer strikten Auslegung, und.E2
konvertiert es in ein rvalue, so dass es zu undefiniertem Verhalten für die schwache interpretation.Folgt auch, dass es zu undefiniertem Verhalten direkt aus (§9.3.1/1):
Mit statischen Funktionen, die strenge vs. schwache interpretation macht den Unterschied. Streng genommen, es ist nicht definiert:
Ist, es wird bewertet, gerade als ob es waren nicht statisch und wir wieder einmal dereferenzieren eines null-Zeigers mit
(*(E1)).E2
.Jedoch, weil
E1
ist nicht in einer statischen member-Funktion aufrufen, wenn wir mit der schwachen Auslegung der Aufruf ist gut definiert.*(E1)
Ergebnisse in ein lvalue ist, wird die statische Funktion gelöst ist, werden die*(E1)
wird verworfen, und die Funktion aufgerufen wird. Es ist kein lvalue-rvalue-Konvertierung, so gibt es kein Undefiniertes Verhalten.In C++0x als der n3126, die Ambiguität bleibt. Für jetzt, sicher sein: verwenden der strengen Auslegung.
Könnten Sie ein wenig zu klären? Insbesondere mit Ihrem "Thema geschlossen" und "aktives Thema" links, was das Thema zahlen? Auch, wenn dies eine geschlossene Frage, was genau ist die ja/Nein-Antwort für statische Funktionen? Ich fühle mich wie ich bin fehlt der Letzte Schritt bei dem Versuch, zu verstehen, Ihre Antwort.
Ich glaube nicht, dass GMG-Mangel 315, ist als "geschlossen", wie seine Präsenz auf der "geschlossenen Fragen" Seite impliziert. Die Logik sagt, dass es erlaubt sein sollte, weil "
*p
ist nicht ein Fehler, wennp
ist null, es sei denn, der lvalue wird in eine rvalue." Jedoch, stützt sich auf das Konzept des "leeren lvalue", und das ist Teil der vorgeschlagenen resolution zu CWG defekt 232, aber die wurde nicht angenommen. Also, mit der Sprache sowohl in C++03 und C++0x, Dereferenzierung der null-Zeiger ist noch nicht definiert, auch wenn es kein lvalue-rvalue-Konvertierung.Durch mein Verständnis, wenn
p
waren eine hardware-Adresse, die auslösen würde manche Aktion, wenn Sie Lesen, aber nicht erklärtvolatile
die Aussage*p;
wäre nicht erforderlich, aber wäre es erlaubt, tatsächlich Lesen, dass die Adresse; die Angabe&(*p);
wäre allerdings verboten, dies zu tun. Wenn*p
warenvolatile
, die Lesen erforderlich wäre. In jedem Fall, wenn der Zeiger ungültig ist, kann ich nicht sehen, wie die erste Anweisung wäre nicht zu Undefiniertem Verhalten, aber ich kann auch nicht sehen, warum die zweite Aussage wäre.".E2 konvertiert es in ein rvalue, " - Ähm, Nein ist es nicht
InformationsquelleAutor GManNickG
Offensichtlich undefiniert bedeutet, dass es nicht definiert, aber es kann auch manchmal vorhersehbar sein. Die Informationen, die ich bin, zu bieten, sollte man nie verlässlich funktionierenden code, da ist es sicher nicht garantiert, aber es könnte nützlich sein, wenn Sie Debuggen.
Könnte man denken, dass der Aufruf einer Funktion auf ein Objekt-pointer-Dereferenzierung der Zeiger und verursachen UB. In der Praxis, wenn die Funktion nicht den virtuellen, der compiler konvertiert haben, zu einem einfachen Funktionsaufruf übergeben den Zeiger als ersten parameter diese, unter Umgehung der dereferenzieren und die Schaffung einer Zeitbombe für den aufgerufenen member-Funktion. Wenn Sie die member-Funktion nicht auf alle member-Variablen oder virtuelle Funktionen, es könnte tatsächlich gelingen ohne Fehler. Denken Sie daran, dass Erfolg fällt in das Universum von "undefined"!
Microsoft die MFC-Funktion GetSafeHwnd tatsächlich beruht Sie auf dieses Verhalten. Ich weiß nicht, was Sie geraucht wurden.
Wenn Sie den Aufruf einer virtuellen Funktion, die Zeiger müssen aufgelöst werden, um auf die vtable, und für sicher, Sie gehen zu bekommen UB (wahrscheinlich ein Absturz aber denken Sie daran, dass es keine Garantien).
es ist schon ein paar Jahre her, seit ich schaute auf den code für
GetSafeHwnd
es ist möglich, dass Sie haben Sie erhöht, da dann. Und vergessen Sie nicht, dass Sie insider-wissen auf den compiler arbeiten!Ich bin Angabe eines Beispiels eine mögliche Umsetzung, die die gleiche Wirkung haben, was bedeutet es wirklich zu tun ist, um reverse Engineering mit einem debugger 🙂
"Sie haben insider-wissen über den compiler arbeiten!" - die Ursache des ewigen ärger für Projekte wie MinGW, der Versuch an, damit g++ zum kompilieren von code, der Aufrufe der Windows-API
Ich denke, wir würden alle einig, das ist unfair. Und weil das so ist, ich denke auch, es gibt ein Gesetz über die Kompatibilität, die macht es ein klein wenig illegal zu halten.
InformationsquelleAutor Mark Ransom