Der this-Zeiger im if



  • @Nexus & @beispiellos
    Sieht beides nach korrektem C++ ohne UB aus.



  • Welches Verhalten erwartest du denn bei der Dereferenzierung eines Nullzeigers?



  • @nwp3: Jackpot! Das Beispiel habe ich von hier, mit Kommentar vom Komitee.

    Eine Referenz auf 0 ist nicht per se UB, erst wenn auf sie irgendwie zugegriffen wird.

    Also ist es vollkommen richtig, this auf 0 zu testen und erst nachher darauf zuzugreifen 😮



  • Aus meiner Praxis: Ich lasse goto einmal im Jahr stehen. Ich lasse switch vielleicht zweimal im Jahr stehen. Aber eine Prüfung auf this==0 nicht einmal überhaupt.
    Ich fürchte, das muss man nur machen, wenn man das Gesamtkonzept verloren hat und versucht, Zugriffsfehler im Nachhinein an den Symptomen zu heilen.

    Ich stelle diesem Stil eine schlechte Prognose aus.



  • Nexus schrieb:

    Welches Verhalten erwartest du denn bei der Dereferenzierung eines Nullzeigers?

    Es gibt mit an Sicherheit grenzender Wahrscheinlichkeit einen Segfault, aber das ist nicht der Punkt. Das Programm wird von jedem korrekten C++-Compiler korrekt übersetzt und tut genau was da steht, einen Nullpointer dereferenzieren.
    Steht denn im Standard dass man den Nullpointer nicht dereferenzieren darf? Was wenn ein Mikrokontroller sein bisschen RAM bei 0 anfangen lässt und darauf hardwaretechnisch auch zugreifen kann, dürfte man das laut Standard trotzdem nicht?



  • Na aber der Standard sagt doch, dass this mit dem Zeiger auf das Objekt belegt ist, in einer nicht-statischen Klassenmethode. 0 kann aber doch kein gültiger Zeiger auf ein Objekt sein?
    Immer diese Grenzfälle...



  • nwp3 schrieb:

    Nexus schrieb:

    Welches Verhalten erwartest du denn bei der Dereferenzierung eines Nullzeigers?

    Es gibt mit an Sicherheit grenzender Wahrscheinlichkeit einen Segfault, aber das ist nicht der Punkt. Das Programm wird von jedem korrekten C++-Compiler korrekt übersetzt und tut genau was da steht, einen Nullpointer dereferenzieren.
    Steht denn im Standard dass man den Nullpointer nicht dereferenzieren darf? Was wenn ein Mikrokontroller sein bisschen RAM bei 0 anfangen lässt und darauf hardwaretechnisch auch zugreifen kann, dürfte man das laut Standard trotzdem nicht?

    Nee neen ee. Der Nullzeiger ist ein Zeiger, der unabhängib von der internen Repräsentaion anzeigt, daß da nix ist. Er muss intern nicht 0x0000000 sein. Kann auch ganz anders. Dann tut halt der Compiler für das Embedded-Ding (void*)0 nach 0xdeadbeef mappen oder so. Alles möglich.



  • volkard schrieb:

    Aus meiner Praxis: Ich lasse goto einmal im Jahr stehen. Ich lasse switch vielleicht zweimal im Jahr stehen. Aber eine Prüfung auf this==0 nicht einmal überhaupt.
    Ich fürchte, das muss man nur machen, wenn man das Gesamtkonzept verloren hat und versucht, Zugriffsfehler im Nachhinein an den Symptomen zu heilen.

    Ich stelle diesem Stil eine schlechte Prognose aus.

    genau das hatte ich damals dem Projektleiter auch gesagt 👍



  • #include <cstdio>
    Test* t;
    t = 0;
    t->f(); //korrekt, wenn auch nicht sinnvoll
    t = nullptr;
    t->f(); //UB
    t = NULL;
    t->f(); //???
    

    Soweit richtig? Nummer 3 wäre dann wohl auch UB.



  • volkard schrieb:

    Nee neen ee. Der Nullzeiger ist ein Zeiger, der unabhängib von der internen Repräsentaion anzeigt, daß da nix ist. Er muss intern nicht 0x0000000 sein. Kann auch ganz anders. Dann tut halt der Compiler für das Embedded-Ding (void*)0 nach 0xdeadbeef mappen oder so. Alles möglich.

    wie man z.B. unter 4.10 Pointer conversions nachlesen kann, kann man den Nullpointer mit "(int) 0" vergleichen



  • nwp3 schrieb:

    Steht denn im Standard dass man den Nullpointer nicht dereferenzieren darf?

    Ja, im alten Standard war die Nullzeiger-Dereferenzierung undefiniertes Verhalten. Eine Ausnahme war die Existenzgrundlage von std::bad_typeid . Ich weiss nicht, in wie fern sich C++11 hier geändert hat, jedenfalls gab es mal diesen Vorschlag zur Klarstellung.

    volkard schrieb:

    Ich fürchte, das muss man nur machen, wenn man das Gesamtkonzept verloren hat und versucht, Zugriffsfehler im Nachhinein an den Symptomen zu heilen.

    Sehe ich auch so. Generell halte ich die Notwendigkeit, Zeiger immer gleich sofort auf Null zu setzen (nach delete -- oder noch schlimmer -- im Destruktor) und jeweils zu prüfen, für fragwürdig. Oft ist das ein Hinweis darauf, dass man sich über die Gültigkeit seiner Zeiger nicht im Klaren ist. Hier habe ich etwas Ausführlicheres dazu geschrieben.



  • nwp3 schrieb:

    #include <cstdio>
    Test* t;
    t = 0;
    t->f(); //korrekt, wenn auch nicht sinnvoll
    t = nullptr;
    t->f(); //UB
    t = NULL;
    t->f(); //???
    

    Soweit richtig?

    Nein. Der Wert von t ist nach den Zuweisungen durch 0 , NULL und nullptr jeweils derselbe. Das Makro NULL ist in C++ gerade als 0 definiert, nullptr ist die typsichere C++11-Variante.



  • Nexus schrieb:

    Generell halte ich die Notwendigkeit, Zeiger immer gleich sofort auf Null zu setzen (nach delete -- oder noch schlimmer -- im Destruktor) und jeweils zu prüfen, für fragwürdig. Oft ist das ein Hinweis darauf, dass man sich über die Gültigkeit seiner Zeiger nicht im Klaren ist.

    Ein

    delete p; p=0;
    

    bzw

    safeDelete(p);//template!
    

    ist ein sicheres Zeichen dafür, daß ich diesem Projekt nicht beitrete. Und wenn sie mich genug anflehen, nehme ich mir eine Minute und sichte den bisherigen Code und finde ein new[]/delete-Paar oder eine integer-Division durch 0 oder eine Endlosschleife. Gamecoders halt.



  • Nexus schrieb:

    Ja, im alten Standard war die Nullzeiger-Dereferenzierung undefiniertes Verhalten. Eine Ausnahme war die Existenzgrundlage von std::bad_typeid . Ich weiss nicht, in wie fern sich C++11 hier geändert hat [...]

    Nexus schrieb:

    Das Makro NULL ist in C++ gerade als 0 definiert, nullptr ist die typsichere C++11-Variante.

    In C++11 hat sich beides geändert. Nullzeiger-Dereferenzierung ist erlaubt, solange es eine prvalue bleibt (und nicht in decltype oder so auftritt).

    Und NULL darf auch nach nullptr definiert sein.

    Davon abgesehen tritt in meinem Code nicht einmal "delete p" auf (ausser vielleicht in Custom Deletern für Smart-Pointer).



  • int main(){
    	Test *t = reinterpret_cast<Test *>(sizeof t);
    	t[-1].f();
    }
    

    UB?
    Wenn t = 0 nichts mit der Speicheradresse 0 zu tun hat dann müsste ja folgendes gelten:

    Test *t = 0;
    t++; //kann man den 0-Pointer überhaupt incrementieren?
    assert(t == 0);
    


  • Hi

    Ok ein bisschen viel Text und allem kann ich nicht folgen :).
    Ich beziehe mich auf das ähneliche Bspiel von dd++

    Zuerstmal if(!this) ein bisschen "augenfreundlicher" geschrieben steht für if(this==0) :).
    Das heißt doch, das tritt nur dann auf wenn es für adr keine Instanz gibt richtig ?

    Also wenn ich sowas mache:

    struct adr
    {
       string name;
       string adresse;
       adr *next;  
       ///....
       void del()
       {
          if(!this) return;
          if(next)
          {
            ....
          }
       }
    }
    
    int main()
    {
       adr adress*;
       adress->del();
    
       retrun 0;
    }
    

    man "will"/wollter sich vor einer Exception schützen wenn jemand sowas, wie oben in "main" gezeigt, macht ?
    (Ob das jetzt gut oder schlecht ist sei mal dahingestellt.)

    Bzw

    int main()
    {
       adr adress* = new adr();
       // ...
       // hier passiert ganz viel 
       //
       delete adress;
       // ...
       // hier passiert ganz viel 
       //
       adress->del();
    
       retrun 0;
    }
    

    Nochmal ob das jetzt gut oder schlecht ist sei mal dahingestellt.

    Grüße



  • Nicht so ganz. Wenn du auf this == 0 testest, dann muss this auch 0 sein damit das true wird. C++ hat keine Ahnung ob ein Pointer gültig ist oder nicht.

    adr *adress;
    adress->del(); //adress ist höchstwahrscheinlich nicht 0, daher bringt der 0-Test nichts
    
    adr *adress = new adr;
    delete adress;
    adress->del(); //adress ist definitiv nicht 0, daher bringt der 0-Test nichts
    


  • beispiellos schrieb:

    In C++11 hat sich beides geändert. Nullzeiger-Dereferenzierung ist erlaubt, solange es eine prvalue bleibt (und nicht in decltype oder so auftritt).

    Erzähl mehr.



  • volkard schrieb:

    beispiellos schrieb:

    In C++11 hat sich beides geändert. Nullzeiger-Dereferenzierung ist erlaubt, solange es eine prvalue bleibt (und nicht in decltype oder so auftritt).

    Erzähl mehr.

    Puh, dann bin ich nicht der einzige der sich dumm vorkommt 🕶



  • Nexus schrieb:

    volkard schrieb:

    Ich fürchte, das muss man nur machen, wenn man das Gesamtkonzept verloren hat und versucht, Zugriffsfehler im Nachhinein an den Symptomen zu heilen.

    Sehe ich auch so. Generell halte ich die Notwendigkeit, Zeiger immer gleich sofort auf Null zu setzen (nach delete -- oder noch schlimmer -- im Destruktor) und jeweils zu prüfen, für fragwürdig. Oft ist das ein Hinweis darauf, dass man sich über die Gültigkeit seiner Zeiger nicht im Klaren ist.

    Auch wenn ich es ebenso sehe: Wenn man Altcode übernimmt, und nicht die Zeit für eine saubere Korrektur bekommt, ist so etwas dennoch ein Weg um Fehler zu finden.

    Ich habe mehr als einmal an einer "historisch gewachsenen" Anwendung gearbeitet, und nur in seltenen Fall (wie z.B. in meiner aktuellen Firma) erhält man auch die Zeit um Code wirklich zu korrigieren (selbst wenn man es wohl dennoch versucht, geht dies ansonsten nur immer Häppchenweise).

    Ich kenne Projekte bei dem ein Programmierchef der Meinung das alles möglichst public und static zu deklarieren und hat solche Variablen auch doppelt und dreifach zu verwenden. Und dies galt auch für Zeiger (wenigstens konnte man davon ausgehen das diese wenn Objekte gelöscht wurden sind genullt wurden, so das man diese gegen null prüfen konnte). Dies ganze wurde noch dadurch verschlimmbessert, dass der zudem die Meinung galt, das der Benutzer keine Fehlermeldungen sehen sollte (lieber mal auf gewissen Inkonsistenzen weiter arbeiten). Ich habe eines für mein Leben gelernt: Wenn ich jemals wieder an so ein Projekt komme werde ich aktiv umgehend eine neue Stelle suchen.


Anmelden zum Antworten