2009-10-06 3 views
5

Im folgenden Code-Schnipsel zu machen, obwohl Zeiger nicht der Anruf erfolgreich gemacht wird initialisiertWarum bin ich in der Lage einen Funktionsaufruf mit einem ungültigen Klassenzeiger

temp *ptr; 
ptr->func2(); 

Es ist aufgrund der Sprache C++ Eigentum oder es ist VC++ 6 Compiler, der schlecht spielt?

class temp { 
public: 
    temp():a(9){} 
    int& func1() 
    { 
     return a; 
    } 
    bool func2(int arg) 
    { 
     if(arg%2==0) 
      return true; 
     return false; 
    } 
    int a; 
}; 

int main(int argc, char **argv) 
{ 
    temp *ptr; 
    int a; 
    cin>>a; 
    if(ptr->func2(a)) 
    { 
     cout<<"Good poniner"<<endl; 
    } 
    ptr->func1(); // Does not crash here 
    int crashere=ptr->func1();// But does crash here 
    return 0; 
} 
+1

Es ist erwähnenswert, dass VC6 dem C++ - Standard vorausgeht. Sehen Sie das für mehr Gründe, VC6 nicht zu benutzen ... http: //www.jasonbadams.net/20090119/why-you-shouldnt-vc6/ – Twisol

Antwort

13

Die C++ Compiler verhindert nicht von uninitialised Zeigern, und obwohl die Ergebnisse nicht definiert sind, ist es normal für Compiler in der realen Welt Code zu erzeugen, ignoriert die Tatsache, dass der Zeiger nicht initialisiert wird.

Dies ist einer der Gründe, warum C++ relativ zu einigen anderen Sprachen schnell und (vergleichsweise) gefährlich ist.

Der Grund, warum Ihr Aufruf an func2 erfolgreich ist, ist, dass es seinen this Zeiger nicht berührt. Der Zeigerwert wird nie verwendet und kann daher kein Problem verursachen. In func1 Sie tun verwenden Sie den this Zeiger (auf eine Member-Variable zugreifen), weshalb dieser abstürzt.

0

Ich vermute, dass ptr->func2() ist weg optimiert, da es immer wahr zurückgibt. Aber wie Richie sagt, ist die Verwendung des Zeigers in diesem Fall in Ordnung, da Sie keine Instanzdaten berühren (a) und als solche gibt es nichts, worauf Sie stoßen könnten.

+0

Es kann nicht weg optimiert werden, weil es mehr Code abhängig ist (if-Anweisung). –

+0

Es * kann * weg optimiert werden. Ein Optimierer kann davon ausgehen, dass jeder Codepfad, der zur Laufzeit zu einem nicht definierten Verhalten führen würde, nicht erreichbar ist. Dieser Codepfad kann daher rückwärts vom nicht definierten Verhalten zum letzten Zweig entfernt werden. Als Ergebnis kann ein Programm, das UB unabhängig von der Eingabe erzeugen muss, auf 'int main() {} ' – MSalters

0

Es funktioniert nicht. Es ist nur ein Glück, dass Ihre Anwendung nicht abstürzt, wenn Sie erwarten, dass sie abstürzt. Es ist kein Feature, sondern ein Nebeneffekt des Compilers, der den Funktionsaufruf scheinbar funktionieren lässt.

+1

Nicht wahr optimiert werden. 'func2' verwendet seinen 'this'-Zeiger nicht, damit es niemals abstürzt. – RichieHindle

+0

Nicht sicher, warum dies abgelehnt wurde. Man könnte sich böse (oder vielleicht hilfreiche) Compiler-Writer vorstellen, die es tatsächlich zum Absturz bringen würden (immerhin kann statisch festgestellt werden, dass der Pointer nicht initialisiert ist, und Compiler warnen davor). Also ist dieses "nie" von RichieHindle falsch (vielleicht sind Compiler noch nicht auf diesem Niveau). Ich würde es einfach nicht "Glück" nennen, dass Programme nicht sofort abstürzen, wenn der Code falsch ist. – UncleBens

+1

mein Denken war, dass obwohl temp-> func2() zu func2 (temp) auflösen wird, ist es immer noch nicht korrekt. Interessanter Hinweis, wenn Sie AppVerifier in Windows XP mit diesem Code ausführen, stürzt es auf temp-> func2() ab. –

7

Dies ist vollständig Compiler-abhängig und undefiniertes Verhalten nach dem Standard. Du solltest dich nicht darauf verlassen.

Anruf zu func2() gelingt, weil die genaue Methode wird bei der Kompilierung zu nennen bekannt (der Anruf ist nicht virtuell) und das Verfahren selbst nicht dereferenzieren diesen Zeiger. So ungültig ist in Ordnung.

ptr->func1(); // This works 

, weil der Compiler angewiesen wird, den Verweis auf das Klassenmember zurückzugeben. Dazu fügt er einfach den Offset des Elements dem ungültigen Wert dieses Zeigers hinzu, und das erzeugt noch einen anderen ungültigen Zeiger (Referenz, die fast identisch ist).

int crashere=ptr->func1();// Crashes here 

, weil jetzt der Compiler angewiesen wird, den Wert über diese ungültige Referenz abzurufen, und das das Programm abstürzte.

3

C++ hat den Begriff des nicht definierten Verhaltens. Die Verwendung eines nicht initialisierten Zeigers ist ein häufiges Beispiel für ein solches nicht definiertes Verhalten. Aus Gründen der Performance setzt der C++ - Standard keinerlei Einschränkungen auf das mögliche Ergebnis. Das heißt, das Formatieren der Festplatte ist vollkommen akzeptabel.

+2

In Sowjetrussland deriterisiert Zeiger * Sie *. –

0

Sie können die Methoden mit nicht initialisierten Zeigern aufrufen. Es funktioniert entweder ptr-> func1(); oder int crashere = ptr-> func1(); //

Verwandte Themen