Ausgabe von char *name über cout ?



  • Naja, wir würden da jetzt wohl ewig drüber streiten...



  • Ein reinterpret_cast kann z.B. auch einen int in einen Zeiger casten. Oder zwischen Zeigern casten, die gar nichts miteinander zu tun haben ( double** zu MyClass* ). Das ist in den meisten Fällen ein Fehler (z.B. Schreibfehler) und in noch mehr Fällen schlechter Stil/Designfehler. Mit static_cast hätte es nicht kompiliert. Deshalb sollte man reinterpret_cast nur benutzen, wenn man sich sicher ist, dass man genau den auch braucht. Natürlich ist static_cast nicht langsamer als reinterpret_cast , die Konvertierungslogik wird nur dann angewendet, wenn es Sinn ergibt (z.B. int zu float ), bei Pointern, wo in den meisten Fällen sich das Bitmuster nicht ändert, wird auch nichts gemacht. Übrigens kann auch reinterpret_cast z.B. bei Pointerkonvertierungen Veränderungen vornehmen, also die Bitmuster von Original und Konvertat müssen nicht übereinstimmen (genauer gesagt ist das Verhalten sogar implemention-defined).



  • Decimad schrieb:

    Naja, wir würden da jetzt wohl ewig drüber streiten...

    Ich hoffe mal nicht. Weil das keine Philosophische Sache ist, sondern Grundwissen 😕



  • Ihr legt mir hier alle irgendwie totales Unwissen in den Mund, weil ich der Meinung bin, ein reinterpret_cast um einen char* kurz als void* zu behandeln ist ein legitimes Vorgehen.
    Ich werde dieses Sprachelement einfach nie wieder erwähnen hier! Vielleicht könnt ihr das ja auch verpixeln, damit bloß niemand die Buchstabenkombination lesen kann 😉



  • Decimad schrieb:

    Ihr legt mir hier alle irgendwie totales Unwissen in den Mund, weil ich der Meinung bin, ein reinterpret_cast um einen char* kurz als void* zu behandeln ist ein legitimes Vorgehen.

    Es ist falsch. static_cast ist die korrekte Vorgehensweise. Da gibt es einfach nichts zu diskutieren.

    Du verwendest ja auch keinen reintrepret_cast um von double nach float zu kommen, oder?



  • Nein! Dort will ich ja die mächtigkeit von static_cast auch ausnutzen!



  • Es ist falsch. static_cast ist die korrekte Vorgehensweise. Da gibt es einfach nichts zu diskutieren.



  • Ich sehe halt den Fehler nicht, darum lasse ich es hier auf den Streit jetzt ankommen. 😉 Was ist daran falsch? Dass man auch static_cast hätte hernehmen können macht den Umstand, dass reinterpret_cast verwendet wurde zu einem Fehler? Ist finde ich keine gute Begründung 😉



  • Decimad schrieb:

    Dass man auch static_cast hätte hernehmen können macht den Umstand, dass reinterpret_cast verwendet wurde zu einem Fehler?

    Ja.
    Wenn Du aber sagst, daß es nicht im naturwissenschaftlichen Sinne falsch ist, weil der Compiler es ja frißt, hast Du natürlich recht. Genau genommen ist es nur ein Verbrechen.



  • Dass der Compiler es frisst, muss ja noch lange nicht bedeuten, dass es auch das tut, was ich will, daher werde ich das nicht als Grund anführen! Aber wenn du mir jetzt zeigen kannst, dass es nicht das tut, was will, werde ich eure etwas forschen "Angriffe" so hinnehmen!



  • Es gibt einen ganz klaren objektiven Fakt: der Standard überlässt das Verhalten von reinterpret_cast weitestgehend der Implementierung. D.h., der Cast von char* zu void* könnte bei einer standardkonformen Implementeirung auch z.B. immer 0 liefern. Oder immer 0x123456. Oder das binäre Komplement des Originalzeigers. Die Implementierer sind lediglich angehalten, das Verhalten umzusetzen, das der Nutzer generell erwartet.
    Bei static_cast hingegen ist das Verhalten definiert.

    Darüber hinaus gibt es noch stilbezogene Gründe (reinterpret_cast sollte man wenn immer möglich vermeiden), die will ich jetzt aber nicht groß aufführen. Und im Übrigen kann man die Aussage, dass static_cast "mächtiger" als reinterpret_cast wäre, eigentlich nicht stehen lassen. Beide Cast unterscheiden sich vorwiegend in den erlaubten Konvertierungen und reinterpret_cast übernimmt im Allgemeinen das "Low-Level-Zeug", was man sehr selten wirklich braucht. Außerdem suggeriert die Aussage in dem Fall, dass static_cast langsamer wäre, weil da mehr Logik drin sei. Das stimmt aber nicht.



  • Naja, wie wird denn laut Standard nun ein static_cast von char* auf void* definiert? Kann mir gar nicht vorstellen, dass hier nicht genauso vage auf eine Implementationsabhängigkeit verwiesen wird.

    Mit Mächtigkeit wollte ich zudem nie ausdrücken, dass der cast "langsamer" wäre, das wurde mir in den Mund gelegt.



  • char* auf void* ist so definiert, dass das Zurückcasten des void*-Zeigers in einen (cv) char*-Zeiger wieder den Originalzeiger ergibt. Wie schon erwähnt ist das bei reinterpret_cast nicht der Fall. Da man aber void*-Zeiger generell zur typenlosen Übertragung von Objekten in Schnittstellen nimmt und nicht unbedingt für die Ausgabe des Zeigerwerts, ist das genau die Eigenschaft, die man braucht.



  • Decimad schrieb:

    Naja, wie wird denn laut Standard nun ein static_cast von char* auf void* definiert? Kann mir gar nicht vorstellen, dass hier nicht genauso vage auf eine Implementationsabhängigkeit verwiesen wird.

    Sollte aus irgendeinem Grund ein T* anders als ein U* sein, wäre static_cast korrekt und reinterpret_cast falsch. Denken wir zB an far Pointer oder eeprom Pointer etc. Das sind andere Zeiger als "normale" zB near Pointer oder ram pointer. Hier wäre ein reinterpret_cast falsch und würde zu falschem Code führen während ein static_cast den Fehler Melden würde oder die Konvertierung korrekt machen würde (je nachdem um was für unterschiedliche Zeiger es sich hier handelt).

    Deshalb ist reinterpret_cast hier falsch. Es tut hier zufällig das was du willst, aber du kannst das nicht generalisieren. static_cast ist hier in jeder situation korrekt, reinterpret nur in einigen.

    reinterpret sagt ja: "trust me on this one" - sprich der compiler soll einfach tun und nicht denken. Dh wir deaktivieren hier absichtlich Sicherheitsmechanismen. Und wofür?



  • cout << (void*)name;
    

    spart sogar tipparbeit :p



  • C-Stil ftw schrieb:

    cout << (void*)name;
    

    spart sogar tipparbeit :p

    Das kann aber leicht nach hinten losgehen:

    struct base {};
    struct derived : base {};
    struct not_derived {};
    
    base *b;
    (derived*)b;      // das selbe wie static_cast
    (not_derived*)b;  // das selbe wie reinterpret_cast
    
    base *const b2;
    (not_derived*)b2; // eine Mischung aus const_cast und reinterpret_cast
    

    Der Schreibweise allein kann man also nicht ansehen, welcher Cast tatsächlich ausgeführt wird.



  • C-Stil ftw schrieb:

    cout << (void*)name;
    

    spart sogar tipparbeit :p

    Entspricht in etwa einem reinterpret_cast mit allen negativen Konsequenzen!



  • So, jetzt mal tacheles hier. Die ursprüngliche Aufgabe war es, den Wert eines Pointers to char auf der Konsole auszugeben. Ich war mir bewusst, dass unterschiedliche Plattformen evtl. abweichende Zeigerrepräsentationen bieten. Es ist schön und gut, dass static_cast garantiert, dass typ* -> void* -> typ* gilt, allerdings ist es nicht das, was ich hier möchte. Ich möchte nämlich den char* ausgeben, kein void* der nach einem weiteren cast wieder sicher zu einem char* würde. Daher habe ich den cast gewählt, von dem ich am wenigsten erwarten würde, dass er irgendeine implizite konvertierung durchführen würde, in diesem Fall ein reinterpret_cast.
    Keinesfalls ist es als Zufall zu betrachten, dass der entsprechende Quelltext das tut, was er tut. Es wäre eher Pech, wenn er das nicht tut.
    Die Anforderung war zu keinem Zeitpunkt, dass man den Zeiger hinterher noch sicher dereferenzieren kann oder ähnliches. Es ging darum, bei der Ausgabe so nah wie möglich am Originalwert zu bleiben, ich versprach mir durch einen reinterpret_cast genau das.

    Eure Angriffe nach dem Motto "indiskutabel" auf mein "Grundwissen" haben mich schon etwas enttäuscht, ich bin hier noch niemanden auf eine solche Art und Weise persönlich angegangen, bei dem ich meinte, ein Gewisses Interesse zu verspüren.



  • Decimad schrieb:

    ! Vielleicht könnt ihr das ja auch verpixeln, damit bloß niemand die Buchstabenkombination lesen kann 😉

    warum? das ganze per reinterpret_cast<void*> zu verändern, dass wir nur addresse sehen können reicht doch 😉 😃



  • Decimad schrieb:

    So, jetzt mal tacheles hier. Die ursprüngliche Aufgabe war es, den Wert eines Pointers to char auf der Konsole auszugeben. Ich war mir bewusst, dass unterschiedliche Plattformen evtl. abweichende Zeigerrepräsentationen bieten. Es ist schön und gut, dass static_cast garantiert, dass typ* -> void* -> typ* gilt, allerdings ist es nicht das, was ich hier möchte.

    Doch genau das möchtest du. Denn wenn das nicht gilt, dann ist der Wert von dem void* ja falsch.

    Wenn der char* zB ein eeprom Zeiger wäre und void* ein ram Zeiger, wäre die Adresse die du ausgibst schlicht falsch.

    Wenn A->B->A nicht gilt, dann ist die Konvertierung A->B oder B->A falsch. Wenn A->B falsch ist, ist B falsch. Und genau das Problem hast du hier. Sollte B->A das Problem sein wäre der Code korrekt.

    Nur du kannst nicht garantieren dass wenn A->B->A nicht gilt, dennoch A->B gilt.

    Deshalb brauchst du hier einen static_cast.


Anmelden zum Antworten