Methode um (zB) unsigned int in bytes aufzuspalten



  • Ja, 3.10/15 war mir eigentlich bekannt. Ich hatte im union -Kontext einfach nicht an den char/unsigned char Fall gedacht, sondern mich nur an all die anscheinend eher pauschalen Auslegungen erinnert, die man überall finden kann. Beispielsweise vom 22.1.2009 aus comp.lang.c++:

    James Kanze schrieb:

    union
     {
       long l;
       bytes b[4]; 
     } u;
    

    That's formally worse, since it results in undefined behavior if you access b when the last value was written to l. (In practice, which is better depends on the compiler---some compilers ignore aliasing which results from a reinterpret_cast.)

    Man findet Aussagen in dieser Richtung en masse.

    Aber ich muss schon sagen, dass Deine Ausführung "schwer verdaulich" ist. Ich werde mir 3.9 und 3.10 später nochmal anschauen...

    Gruß,
    SP


  • Mod

    Sebastian Pizer schrieb:

    Aber ich muss schon sagen, dass Deine Ausführung "schwer verdaulich" ist. Ich werde mir 3.9 und 3.10 später nochmal anschauen...

    Das verwundert nicht. Du wirst es auch sonst nirgends so aufgeschrieben finden.

    Sebastian Pizer schrieb:

    Beispielsweise vom 22.1.2009 aus comp.lang.c++:

    James Kanze schrieb:

    union
     {
       long l;
       bytes b[4]; 
     } u;
    

    That's formally worse, since it results in undefined behavior if you access b when the last value was written to l. (In practice, which is better depends on the compiler---some compilers ignore aliasing which results from a reinterpret_cast.)

    3.10/15 interessiert nicht, wiedas lvalue, mit dem man den Zugriff durchführt, zustande kommt. Es existiert auch nirgendwo sonst im Standard eine Stelle, die hier irgendwelche Sonderregeln für unions aufstellt. 9.5 sagt etwas über den gespeicherten Wert, wie Zugriffe darauf zu erfolgen haben, entspricht aber den ganz normalen Regeln (abgesehen von dem "common initial sequence" Fall).

    Wo union vs. reinterpret_cast ins Spiel kommt, sind offenbar bestimmt Compilererweiterungen des gcc (und evtl. anderer Compiler). Diese Erweiterungen definieren das Ergbnis von Zugriffen, die nach 3.10/15 eigentlich UB sind, wenn das betreffende lvalue aus einem solchen union-cast hervorgegenagen ist. Das ist selbstverständlich zulässig, nur darf es nicht den Blick auf das, was der Standard verspricht, verstellen. Diese Erweiterung wird ja unter anderem im Linuxkernel oft eingesetzt, man kann also auch davon ausgehen, dass sie erhalten bleibt.



  • TGGC schrieb:

    Ich hab nicht wirklich Lust mit irgendwelchen Zitaten rumzuschmeissen, wenn dich das so interessiert, dann suche sie selbst.

    Habe ich ja. 3.10/15 erlaubt Dir, auf PODs über lvalues vom Typ char und unsigned char zuzugreifen. Allerdings sehe ich im Moment nicht, dass

    unsigned foo(float x) {
       union {
         float fl;    // nix char, nix unsigned char
         unsigned uu; // nix char, nix unsigned char
       };
       fl = x;
       return uu;
     }
    

    funktionieren muss, auch wenn sichergestellt worden ist, dass die Größe von float und unsigned übereinstimmt und dass jedes float Bitmuster ein legales unsigned Bitmuster ergibt. Falls es wirklich so ist, wie viele behaupten (also, dass Du auf nur auf ein union-Element lesend drauf zugreifen darfst, welches zuletzt "beschrieben" worden ist), könnte der Compiler die Zeile "fl = x" wegoptimieren, weil fl danach nie wieder "angefasst" wird. Von daher ist es schon relativ wichtig, was der Standard erlaubt und was nicht.

    Dagegen sehe ich bei

    unsigned foo(float x) {
       unsigned u;
       memcpy(&u,&x,sizeof(u));
       return u;
     }
    

    keine Probleme. Die Regeln in 3.10/15 werden auch nicht verletzt, soweit ich das beurteilen kann.

    TGGC schrieb:

    Mag sein, das du theoretisch Recht hast.

    Ich bin mir da ja selbst gar nicht mehr so sicher -- bin kein "language lawyer".

    TGGC schrieb:

    Und alle diese Member haben die selber Adresse. Und darum hat die union genau den gleichen Effekt, als wenn du einfach den Speicher einer strcut einfach an die Adresse eine anderen struct kopierst, wenn beide structs gleich gross sind.

    Das "und darum" überzeugt mich nicht.

    Gruß,
    SP


  • Mod

    TGGC schrieb:

    In der Praxis zaehlt sowieso nicht, was der Standard vorschreibt, sondern wie es gerade ist.

    In Praxis wird i.d.R. mehr zulässig sein, als dem Standard zufolge zulässig ist. Nur wie viel mehr ist nicht konstant sondern wird weniger (man bedenke z.B. wie viel Code durch die jüngsten Versionen des gcc kaputtgegangen ist, weil striktere Aliasregeln durchgesetzt wurden). Deshalb ist es wichtig, den Standard zu kennen als etwas, worauf man sich verlassen darf.

    @Sebastian Pitzer: Die Einschätzung zu den Beispielen teile ich.
    Möglicherweise geht auch das hier:

    unsigned foo(float x) {
       memcpy(&x,&x,sizeof(x));
       return reinterpret_cast<unsigned&>(x);
     }
    

    Der Funktionsaufruf beim memcpy kann trivialerweise wegoptimiert werden, er müsste allerdings trotzdem für die nötige Verschleierung des Typs des gespeicherten Wertes sorgen. Eine abschließende Meinung habe ich mir dazu aber noch nicht gebildet.


Anmelden zum Antworten