Zugriff auf Klasse über NULL Pointer



  • Hallo!

    Durch Zufall habe ich letztens ein interessantes Verhalten entdeckt:
    Wenn ich über einen NULL-Zeiger auf Methoden/statische Datenelemente zugreife, geht dies ohne weiteres. Ein Absturz findet nur dann statt wenn Zugriff auf Instanzvariablen erfolgt.

    Irgendwie ist das ganze ja klar nachvollziehbar...
    Die Frage: Ist dieses Verhalten nur MS VC++ spezifisch oder irgendwo im Sprachstandard definiert?



  • Über einen Nullpointer? Sowas hier:

    class Klasse
    {
        public:
            void foo();
    };
    
    Klasse* ptr = 0;
    ptr->foo(); // Das hier?
    

    Wie soll das funktionieren? Kann sein, dass er reinzufällig erstmal ptr als Klasse identifiziert und somit allgemeinen Code von foo() ausführt und erst abstürzt wenn er auf den nicht vorhandenen Speicher zugreifen will.

    Aber ich kann mir kaum vorstellen, dass das im Sprachstandard so festgelegt ist.

    Wenn du unbedingt Code ausführen willst der die Klasse allgemein betrifft und nicht ein spezielle Objekt benütze static.

    MfG SideWinder



  • vom Standard wird es nicht erlaubt sein und du erzeugst undefiniertes (unportables) Verhalten. Benutz lieber static-Methoden, die bewirken ja das gleiche nur sind sie portabel.



  • C++ Fetischist schrieb:

    Wenn ich über einen NULL-Zeiger auf Methoden/statische Datenelemente zugreife, geht dies ohne weiteres. Ein Absturz findet nur dann statt wenn Zugriff auf Instanzvariablen erfolgt.

    virtuelle methoden lassen ihn auch abkacken. auch logisch.

    Irgendwie ist das ganze ja klar nachvollziehbar...
    Die Frage: Ist dieses Verhalten nur MS VC++ spezifisch oder irgendwo im Sprachstandard definiert?

    alle mir bekannten compiler sind da wie der VC++. ich denke auch, das wird noch ne weile so bleiben. nur garantiert es keiner.



  • volkard schrieb:

    ich denke auch, das wird noch ne weile so bleiben.

    Wahrscheinlich. Solange in den MFC noch abenteuerliche Konstrukte wie

    HWND CWnd::GetSafeHwnd()
    {
        if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    }
    

    zu finden sind, wird VC++ das wohl auch weiterhin unterstützen (müssen)... 🙄



  • tag schrieb:

    if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    

    rofl!



  • volkard schrieb:

    tag schrieb:

    if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    

    rofl!

    looooooool



  • Ich finde es eher zum heulen



  • Wie soll das denn funktionieren? Eine Klasse, für die kein Speicher existiert, soll in einer Memberfkt. Null zurückgeben? Kann ich mir nicht so ganz vorstellen 😞



  • ethereal schrieb:

    Wie soll das denn funktionieren? Eine Klasse, für die kein Speicher existiert, soll in einer Memberfkt. Null zurückgeben? Kann ich mir nicht so ganz vorstellen 😞

    Das geht schon, dass Problem ist, dass in der Funktion auf einen Member zurückgegrifen wird, dass gibt Probleme.

    Generell geht das, da die Compiler idr. folgenden C++ Code

    class foo {
      int i;
    public:
      foo(int j) : i(j) { }
      void add_10() { i+=10; }
      int do_something() { return 100*std::time(NULL); }
    };
    
    int main() {
      foo bar(10);
      bar.add_10;
      bar.moep();
    }
    

    in ungefähr folgendes C Code äquivalent umsetzen

    struct foo {
      int i;
    };
    
    void foo_constructor(int i,struct foo *this;) { this->i=1; }
    void foo_add_10(struct foo *this) { this->i+=10; }
    void foo_moep(struct foo *this) { return 100*time(); }
    
    int main() {
      struct foo bar;
      foo_constructor(10,&bar);
      foo_add_10(&bar);
      foo_moep(&bar);
    }
    

    und bei foo_moep ist es ja egal, wohin this zeigt, da es ja nicht deferenziert wird.



  • kingruedi schrieb:

    und bei foo_moep ist es ja egal, wohin this zeigt, da es ja nicht deferenziert wird.

    In der Praxis wird der Zeiger zwar nicht dereferenziert,
    der C++ Standard ist in diesem Punkt aber eindeutig. Dort steht, dass foo->bar() dazu führt, dass foo dereferenziert wird. Damit hat der Aufruf einer Memberfunktion über einen Nullzeiger *immer* undefiniertes Verhalten.

    Code wie:

    ((Foo*)0)->func();
    

    ist schlicht und einfach kein gültiges C++.



  • @Hume
    ja natürlich ist das höchst illegal und es macht ja auch keinen Sinn, weil der C++ Standard für so etwas ja extra static-Member eingeführt hat.



  • Dass es Sinn machen kann, zeigt der MS-Code doch. Es ist ja auch größtenteils portabel. Nur halt theoretisch illegal und absolut grottiger Stil, aber IMHO nicht die Aufregung wert, die hier darum gemacht wird 🙄



  • operator void schrieb:

    Dass es Sinn machen kann, zeigt der MS-Code doch.

    Echt? Das sehe ich ehrlich gesagt anders. Ich kann keinen Sinn darin erkennen eine Memberfunktion auf einem Nullpointer aufzurufen, sprich eine Nachricht an ein Objekt zu senden, wo ein Nullpointer ja garantiert *kein* Objekt ist. Da ist imo kein Sinn sondern lediglich eine Brechstange mit der OOP passend geprügelt wird. Oder anders: vielleicht macht es aus technischer Sicht, im Modell (oder der Abtstraktion) aber doch nicht.

    Null Objekte auf der anderen Seite sind durchaus sinnvoll, aber das sind ja auch vollkomen andere Biester.

    Es ist ja auch größtenteils portabel.

    Aus semantischer Sicht imo trotzdem völliger quatsch.

    aber IMHO nicht die Aufregung wert, die hier darum gemacht wird

    Welche Aufregung genau meinst du?



  • @Hume
    Aus einem präexception Standpunkt macht es Sinn.

    Oft wird behauptet, dass man den return value von jeder WinAPI/MFC Funktion auf Fellschlag testen muss, das ist aber nicht ganz richtig. Jede WinAPI/MFC Funktion testet zuerst ob die Parameter gültig sind, das heist im Falle von Pointer, dass sie nicht 0 sind. Wenn sie ungültig sind dann gibt die Funktion einen Fehlerwert zurück, welcher dann an die nächste Funktion weiter gegeben wird welche dann wieder einen Fehlerwert zurück gibt. Also:

    HWND hwnd=CreateWindow(...);
    void*ptr=malloc(24);
    DoWindowStuff(GetWindowInfoStuff(hwnd),...);
    DoOtherStuff(hwnd,...);
    free(ptr);
    CloseWindow(hwnd);
    if(!hwnd)
      MessageBox(0,"CreateWindow failed","Error",16);
    

    Das hier wird also sicher (sollte zumindest) nicht abschmieren, desweiteren ist die Fehlerbehandelung vom Auftritt getrennt und wichtige Funktionen wie free werden aufgerufen. Also eine Art C Exception.

    Heute würde man das mit Exceptions und Destruktoren lösen aber da WinAPI C ist und Exceptions damals noch lange nicht von jedem Compiler unterstützt wurden wurde das so gemacht.

    Das Beispiel ist zwar MFC aber da das nur ein WinAPI Wrapper ist es irgendwie verständlich, dass es Fehler auf die gleiche Art behandelt (verbessert mich wenn ich falsch liege aber MFC benutzt ja keine C++ Exceptions).



  • HumeSikkins schrieb:

    Echt? Das sehe ich ehrlich gesagt anders. Ich kann keinen Sinn darin erkennen eine Memberfunktion auf einem Nullpointer aufzurufen, sprich eine Nachricht an ein Objekt zu senden, wo ein Nullpointer ja garantiert *kein* Objekt ist. Da ist imo kein Sinn sondern lediglich eine Brechstange mit der OOP passend geprügelt wird.

    Vielleicht kommt da teilweise meine Delphi-Vergangenheit durch (TObject.Free tut bei einem Nullzeiger auch nichts). Ich fands einen C+-Hack wie jeden anderen auch. Eigentlich ist das Präfix "Safe" schon ein ganz böses Omen... 🙂

    Es ist ja auch größtenteils portabel.

    Aus semantischer Sicht imo trotzdem völliger quatsch.

    Wollte ich nicht bestreiten. Aber wenns tatsächlich mal geholfen hat, den Code übersichtlich zu halten, ist es immerhin noch ein relativ portabler Hack. Da gibts gefährlichere Missbräuche von UB, nur dass die eben nicht so offensichtlich und dreist sind. Ich dachte, das lol und rofl hätten auch darauf abgezielt. Wenns nur um die Semantik ging, nehme ich mein Posting zurück 🙂



  • hi leute, sorry dass ich den thread nochmal raufhole aber ich hab hier was interessantes 😉

    Ich hab einfach mal meinem Freund 😉 Scott Meyers (Kolumnist im C++ Report und Autor der "Effective" Reihe) geschrieben, als Antwort bekam ich prompt (SOGAR IN DEUTSCH!!!):

    Danke. Es freut mich, daß Sie meine Bücher nützlich gefunden haben.

    I've discovered some strange behavior with null pointer to classes - but I
    could imagine you already know that 🙂
    So, if I call a normal member function of a class via a null-pointer to the class, the function is handled like a static function of the class.
    Is this behavior undefined, not portable, compiler dependent?

    Es ist in prinzip nicht möglich, ein null this-pointer zu haben, weil es nicht möglich sein sollte, ein Mitgliederfunktion ohne ein Objekt aufzurufen. Deswegen ist solche Code bestimmt nicht portable.

    I don't think that it is defined in the c++ standard, but why does MS Visual C++ and g++ support it? Even in the MFC there are constructs like:
    if (this == NULL)
    > return NULL;
    else
    > return m_hWnd;

    Ich habe keine Ahnung, warum solche Code in MFC besteht, aber mir is MFC ein Geheimnis 🙂

    Scott

    Also hier haben wirs nochmal von einem Profi erklärt. 😃

    greetz



  • operator void schrieb:

    Dass es Sinn machen kann, zeigt der MS-Code doch.

    Ich habe ja einen bösen Verdacht wofür die Konstruktion eingeführt wurde

    dynamic_cast<child*>(pointertobase)->DoDirtyStuff()
    

    Wenn so bei Winzigweich Fehler abgefangen werden dürfte so einiges klar werden 😃



  • joX schrieb:

    operator void schrieb:

    Dass es Sinn machen kann, zeigt der MS-Code doch.

    Ich habe ja einen bösen Verdacht wofür die Konstruktion eingeführt wurde

    dynamic_cast<child*>(pointertobase)->DoDirtyStuff()
    

    Wenn so bei Winzigweich Fehler abgefangen werden dürfte so einiges klar werden 😃

    Also da bin ich dann aber doch für:

    try{
      dynamic_cast<child&>(*pointertobase).DoDirtyStuff()
    }catch(bad_cast&){
      ...
    }
    

    Denn wie stelt man beim ersten fest ob es nicht geklappt hat oder nicht?



  • Man sollte prüfen ob ein null-Pointer zurückgegeben wurde.


Anmelden zum Antworten