Seltsamer Fehler beim Herumspielen mit reinterpret_cast



  • Hallo,
    ich habe mich ein wenig mit reinterpret_cast beschäftigt. Dabei wollte ich einen einfachten Code schreiben, der mit die unteren 32 Bit einer long long variablen liefert:

    typedef unsigned long long  ull;
    typedef unsigned int        ui;
    
    int main()
    {
        ull b = 13;
        ui a = *reinterpret_cast<const ui* const>(&b); //ausgabe: 30
    }
    

    Dabei erhält a den falschen Wert (30 statt 13) und es gibt eine Warnung:

    7: warning: dereferencing type-punned pointer will break strict-aliasing rules
    

    Was will mir die Warnung sagen? Was mache ich falsch?

    Schonmal Danke für alle Antworten!



  • Hi,
    das ist ein Bug (?) des GCC in neueren Versionen. Die Konvertierung ist erlaubt und wird in der Praxis keine Probleme bereiten.

    Siehe: http://wiki.alliedmods.net/Metamod:Source_Development#Linux

    -fno-strict-aliasing - By default, GCC does not allow certain type casts which were perfectly legal in previous versions, and are perfectly legal in almost every other C/C++ compiler. These type casts are prevalent and without this option, GCC will generate incorrect code.


  • Administrator

    NeedHelpPlease schrieb:

    Was mache ich falsch?

    Dass du dazu auf reinterpret_cast zurückgreifst. Das will dir zwar die Warnung nicht sagen, aber sage ich 🙂

    unsigned long long b = 13;
    unsigned long a = b & 0xFFFFFFFF;
    

    Was in deinem Fall genau herauskommt, ist im Standard nicht definiert. Bei den meisten Kompiler wird es wohl funktionieren. Allerdings könntest du auch Probleme mit Systemen bekommen, wo Big Endian in Verwendung ist.
    Mein Code ist vollständig definiert. Naja, bis darauf das long long erst im nächsten Standard kommt und die Grössen der Variablen nicht genau definiert ist, aber das ist in deinem Code auch der Fall. Gehen wir daher davon aus, dass es 64 Bit und 32 Bit vorzeichenlose Zahlen sind (;)). Unter dieser Annahme liefert es auch immer die unteren 32 Bit, egal ob dein System nun Big-Endian oder Little-Endian verwendet.

    Aber das könnte man alles noch als Erbsenzählerei hinsetzen, was meiner Meinung nach wichtiger ist, mein Code kann man deutlich besser lesen und man versteht sofort, was gemacht wird.

    Grüssli



  • Dravere schrieb:

    ...

    vs

    theliquidwave schrieb:

    ...

    Auf welche Antwort kann man sich nun verlassen?



  • WerHatNunRecht? schrieb:

    Auf welche Antwort kann man sich nun verlassen?

    Beide nicht.
    http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html


  • Administrator

    volkard schrieb:

    WerHatNunRecht? schrieb:

    Auf welche Antwort kann man sich nun verlassen?

    Beide nicht.
    http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

    Da musst du mir aber zuerst mal erklären, was an meinem Beitrag nicht stimmt. Ich umgehe dieses strict aliasing komplett, indem ich gar nicht mit Zeiger herumhantiere.

    Grüssli



  • Dravere schrieb:

    Da musst du mir aber zuerst mal erklären, was an meinem Beitrag nicht stimmt.

    Die Behaptung, es läge am bösen reinterpret_cast.
    Und die Nahelegung, die Endianess sei das Hauptproblem.


  • Administrator

    volkard schrieb:

    Dravere schrieb:

    Da musst du mir aber zuerst mal erklären, was an meinem Beitrag nicht stimmt.

    Die Behaptung, es läge am bösen reinterpret_cast.
    Und die Nahelegung, die Endianess sei das Hauptproblem.

    Dann hast du meinen Beitrag völlig falsch verstanden. Ich habe mich sogar explizit von der Warnung distanziert. Und mit dem Endian-Problem wollte ich auf ein anderes Problem hinweisen.

    Mir ging es eher darum, dass man sich nicht mit solchen Dingen auseinandersetzen sollte, wenn man es doch ganz einfach und wohl definiert machen kann. reinterpret_cast ist laut Standard in diesem Fall nun mal nicht exakt definiert. Wieso also den reinterpret_cast verwenden, wenn man es einfach, verständlich und korrekt machen kann?

    Grüssli



  • Dravere schrieb:

    volkard schrieb:

    Dravere schrieb:

    Da musst du mir aber zuerst mal erklären, was an meinem Beitrag nicht stimmt.

    Die Behaptung, es läge am bösen reinterpret_cast.
    Und die Nahelegung, die Endianess sei das Hauptproblem.

    Dann hast du meinen Beitrag völlig falsch verstanden.

    Ja, anscheinend.

    Dravere schrieb:

    Mir ging es eher darum, dass man sich nicht mit solchen Dingen auseinandersetzen sollte, wenn man es doch ganz einfach und wohl definiert machen kann.

    Ah, dem kann ich natürlich nicht zustimmen. Natürlich muß man auch das lernen. Und dann nicht benutzen.

    Dravere schrieb:

    reinterpret_cast ist laut Standard in diesem Fall nun mal nicht exakt definiert. Wieso also den reinterpret_cast verwenden, wenn man es einfach, verständlich und korrekt machen kann?

    Ich glaube, daß der reinterpret_cast hier aber sauber funktioniert.
    Versuche mit

    cout<<reinterpret_cast<const ui* const>(&b)
    

    weisen sogar ein wenig in diese Richtung.
    Vielleicht erzeugt erst das Auswerten mit * den Müll. Denselben Müll kann man auch ohne reinterpret_cast erzeugen, zum Beispiel mit dieser union mit den beiden Zeigern drin.


  • Administrator

    volkard schrieb:

    Ah, dem kann ich natürlich nicht zustimmen. Natürlich muß man auch das lernen. Und dann nicht benutzen.

    Gut, da hast du natürlich recht. Deswegen habe ich den Link auch durchgelesen und fand den Inhalt sehr interessant. Aber ich hätte es wohl gar nie so gemacht, wie es der Threadersteller gemacht hat 😉

    volkard schrieb:

    Ich glaube, daß der reinterpret_cast hier aber sauber funktioniert.
    Versuche mit

    cout<<reinterpret_cast<const ui* const>(&b)
    

    weisen sogar ein wenig in diese Richtung.
    Vielleicht erzeugt erst das Auswerten mit * den Müll. Denselben Müll kann man auch ohne reinterpret_cast erzeugen, zum Beispiel mit dieser union mit den beiden Zeigern drin.

    Ja, wenn man pingelig sein will, dann ist erst der Dereferenzierungsoperator schuld. Es ist nämlich wohldefiniert, wenn man etwas in einen anderen Zeiger umbiegt, es wieder zurückbiegt und dann darauf zugreift. Also an einem Beispiel:

    int a;
    char* b = reinterpret_cast<char*>(&a);
    int* p = reinterpret_cast<int*>(b);
    *p = 10; // <- klar definiert und korrekt.
    

    Es ist nur nicht definiert, was passiert, wenn man auf b zugreift. Aber leider wird reinterpret_cast oft dazu verwendet, genau dies zu erreichen, was der Threadersteller will. Weshalb man diese Unsitte nach dem sehr klar ersichtlichen reinterpret_cast benennt, während der tatsächliche Übeltäter, wegen seiner Unscheinbarheit ungeschoren davon kommt ... 🤡

    Grüssli


Anmelden zum Antworten