Unions oder doch Bit-Shift und Bit-Operationen



  • Hallo und schöne Pfingsten an alle,

    im moment Schreibe ich an einer Emulation für einen Zilog Z180 Prozessor. Mit diesem habe ich mir vor ca. 13Jahren ein CP/M-System zusammengelötet.
    Nun habe ich mir gedacht, das System einmal in Software nachzubauen. Das ganze ist bis jetzt soweit, das das Monitor-Programm welches ich damals ins Boot-EPROM gebrannt habe funktioniert.
    Jedoch hätte ich da eine allgemeine Vorgehensfrage. Ich hab schon einiges dazu gelesen, aber möchte nun doch spezifisch einmal die Community dazu hören (lesen).

    Der Prozessor besitzt 8Bit-Register die sich im bedarfsfalle auch als 16Bit-Register benutzen lassen.
    In vielen ähnlichen Projekten werden dazu Bit-Shift Operationen benutzt.

    uint8_t regH;
    uint8_t regL;
    uint16_t regHL;
    
    // H und L zu HL
    regHL = ((regH << 8) + regL);
    
    // H aus HL
    regH = (regHL >>8);
    
    // L aus HL
    regL = (regHL % 0xFF);
    

    ich habe dafür Unions benutzt.

    union {
                uint16_t HL;    // 16Bit Register-Paar HL
                struct {
                    uint8_t L;  // 8Bit Register L (lower 8Bit)
                    uint8_t H;  // 8Bit Register H (upper 8Bit)
                };
            } regHL;
    

    damit kann ich ganz einfach auf die 8- oder 16Bit-Register zugreifen.

    regHL.L = 0x33;
    ...
    regHL.HL = 0x222A;
    ...
    regHL.HL++;
    

    und für das Register-Paar AF welches auch die Flags enthält:

    union {     // 16Bit Register-Paar AF
                uint16_t AF;
                struct {
                    union {
                        uint8_t F;  // 8Bit Register F (lower 8Bit)
                        struct {
                            bool Flag_C : 1;    // 'Carry' Flag
                            bool Flag_N : 1;    // 'Negative' Flag
                            bool Flag_PV : 1;   // 'Parity/Overflow' Flag
                            bool  : 1;
                            bool Flag_H : 1;    // 'Half-Carry' Flag
                            bool  : 1;
                            bool Flag_Z : 1;    // 'Zero' Flag
                            bool Flag_S : 1;    // 'Sign' Flag
                        };
                    };
                    uint8_t A;  // 8Bit Register A (upper 8Bit)
                };
            } regAF;
    

    so kann ich z.B. mit

    regAF.Flag_N = true;
    

    oder mit

    if(regAF.Flag_C) ...
    

    direkt auf die Flags zugreifen oder mit Binäroperationen

    #define Flag_C 0x01
    
    ...
    regF |= Flag_C;
    ...
    if(regF & Flag_C) ...
    

    hantieren zu müssen.

    Das Thema Little- Big-Endian ist mir bewusst.
    Mit würde einfach interessieren wir Ihr das mit den Unions so seht. Aber auch wie es Perfomance Technisch mit den zwei Vorgehensweisen ausschaut.

    Grüße Netzschleicher



  • @Netzschleicher sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    ich habe dafür Unions benutzt.

    Ja, das darfst Du anders als in C in C++ nicht Tunfisch. [class.union]/1 und [basic.life]/1

    Einen union-Member schreiben und dann den anderen lesen ohne den vorher beschrieben zu haben ist nicht. Du müsstest also sicherstellen, daß das nicht vorkommt. Type punning damit ist ein no-no. Es wäre höchstens erlaubt wenn es sich bei den beiden Membern um structs handelt und da auch nur, wenn deren Member zumindest am Anfang der structs in ihren Typen übdereinstimmen. Also zB.

    union foo {
        struct {
            int a;
            char b;
        } bar;
        struct {
            int c;
            char d;
        } qux;
    };
    
    // ...
    
    foo f;
    f.bar.b = 'b';
    std::cout << f.qux.d;
    

    wäre ok. Aber

    union foo {
        struct {
            int a;
            char b;
        } bar;
        struct {
            bool c;  // uups
            char d;
        } qux;
    };
    
    // ...
    
    foo f;
    f.bar.b = 'b';
    std::cout << f.qux.d;
    

    schon gaga. Hilft Dir also nicht.

    @Netzschleicher sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    // L aus HL
    regL = (regHL % 0xFF);
    

    Ökonomischer und sicher auch allgemein üblicher ist da schon bitwise and:

    regL = (regHL & 0x00ff);
    

    Wird zwar jeder Compiler (hoffentlich) sowieso freiwillig von sich aus aus dem modulo machen, aber ...

    #include <cstdint>
    
    using  byte_t = std::uint8_t;
    using  word_t = std::uint16_t;
    using dword_t = std::uint32_t;
    
    word_t get_hi_word(dword_t dword) { return dword >> 16u; }
    word_t get_lo_word(dword_t dword) { return dword & 0x0000ffffu; }
    
    dword_t set_hi_word(dword_t  &dword, word_t word) { return dword = (word << 16u) | get_lo_word(dword); }
    dword_t set_hi_word(dword_t &&dword, word_t word) { return         (word << 16u) | get_lo_word(dword); }
    dword_t set_lo_word(dword_t  &dword, word_t word) { return dword = (get_hi_word(dword) << 16u) | word; }
    dword_t set_lo_word(dword_t &&dword, word_t word) { return         (get_hi_word(dword) << 16u) | word; }
    
    byte_t get_hi_byte(word_t word) { return word >> 8u; }
    byte_t get_lo_byte(word_t word) { return word & 0x00ff; }
    
    word_t set_hi_byte(word_t  &word, byte_t byte) { return word = (byte << 8u) | get_lo_byte(word); }
    word_t set_hi_byte(word_t &&word, byte_t byte) { return        (byte << 8u) | get_lo_byte(word); }
    word_t set_lo_byte(word_t  &word, byte_t byte) { return word = (get_hi_byte(word) << 8u) | byte; }
    word_t set_lo_byte(word_t &&word, byte_t byte) { return        (get_hi_byte(word) << 8u) | byte; }
    
    byte_t get_hi_nibble(byte_t byte) { return byte >> 4u; }
    byte_t get_lo_nibble(byte_t byte) { return byte & 0x0f; }
    
    byte_t set_hi_nibble(byte_t  &byte, byte_t nibble) { return byte = ((nibble & 0x0f) << 4u) | get_lo_nibble(byte); }
    byte_t set_hi_nibble(byte_t &&byte, byte_t nibble) { return        ((nibble & 0x0f) << 4u) | get_lo_nibble(byte); }
    byte_t set_lo_nibble(byte_t  &byte, byte_t nibble) { return byte = (get_hi_nibble(byte) << 4u) | (nibble & 0x0f); }
    byte_t set_lo_nibble(byte_t &&byte, byte_t nibble) { return        (get_hi_nibble(byte) << 4u) | (nibble & 0x0f); }
    
    // a 32-bit general purpose register:
    
    #include <bitset>
    #include <string>
    #include <sstream>
    #include <iomanip>
    
    class gp_reg32_t
    {
    	dword_t ex;  // full Enhanced eXtended register, eg. EAX
    
    public:
    	gp_reg32_t(dword_t value = {}) : ex { value } {}
    
    	operator dword_t& () { return ex; }
    
    	// ok, there is no such "sub"-register in x86 but it might come in handy.
    	word_t get_ex_hi() const { return get_hi_word(ex); }
    	gp_reg32_t& set_ex_hi(word_t value) { set_hi_word(ex, value); return *this; }
    
    	// for consistency
    	word_t get_ex_lo() const { return get_lo_word(ex); }
    	gp_reg32_t& set_ex_lo(word_t value) { set_lo_word(ex, value); return *this; }
    
    	// AX
    	word_t get_x() const { return get_ex_lo(); }
    	gp_reg32_t& set_x(word_t value) { return set_ex_lo(value); }
    
    	// AH
    	byte_t get_hi() const { return get_hi_byte(get_x()); }
    	gp_reg32_t& set_hi(byte_t value) { return set_x(set_hi_byte(get_x(), value)); }
    
    	// AL
    	byte_t get_lo() const { return get_lo_byte(get_x()); }
    	gp_reg32_t& set_lo(byte_t value) { return set_x(set_lo_byte(get_x(), value)); }
    
    	std::string str(std::ios::fmtflags flags = std::ios::showbase | std::ios::fixed | std::ios::hex) const
    	{
    		std::stringstream ss;
    		ss.flags(flags);
    		
    		if (flags & std::ios_base::fixed)
    			ss << std::setfill('0') << std::setw(8);
    
    		ss << ex;
    		return ss.str();
    	}
    
    	std::string bin_str(std::ios::fmtflags flags = std::ios::showbase | std::ios::fixed) const
    	{
    		std::stringstream ss;
    		ss.flags(flags);
    
    		if (flags & std::ios_base::fixed)
    			ss << std::setfill('0') << std::setw(32);
    
    		ss << std::bitset<32>{ ex }
    		   << (flags & std::ios_base::showbase ? "b" : "");
    		return ss.str();
    	}
    
    	// todo: Funktionen schreiben zum setzen oder loeschen Deiner Lieblingsflags.
    };
    
    
    
    #include <iostream>
    
    int main()
    {
    	gp_reg32_t eax;
    	std::cout << eax << '\n';  // 0
    
    	eax = 42;
    	std::cout << eax << '\n';  // 42
    
    	gp_reg32_t ebx { 0xabadf4ce };
    	std::cout << ebx.str() << '\n';  // 0xabadf4ce
    
    	eax = ebx;
    	eax.set_x(0xbeef);
    	std::cout << eax.str() << '\n';  // 0xabadbeef
    
    	eax.set_ex_hi(0xdead);
    	std::cout << eax.str() << '\n';  // 0xdeadbeef
    
    	std::cout << std::hex << set_hi_word(0x1234, set_hi_byte(0x99ff, 0xaa)) << '\n';  // aaff1234
    	
    	gp_reg32_t foo { 0xabcd };
    	foo.set_hi(0xef);
    	std::cout << foo.str() << '\n';  // 000xefcd
    	
    	eax = 0x0012abcd;
    	std::cout << eax.str() << " - " << eax.bin_str() << '\n';  // 0x12abcd - 00000000000100101010101111001101b
    	eax <<= 3u;
    	std::cout << eax.str() << " - " << eax.bin_str() << '\n';  // 0x955e68 - 00000000100101010101111001101000b
    
    	eax = 0;
    	eax |= 0xc9;
    	eax <<= 16;
    	std::cout << eax.str() << " - " << eax.bin_str() << '\n';  // 0xc90000 - 00000000110010010000000000000000b
    
    	eax.set_lo(0xc9);
    	std::cout << eax.str() << " - " << eax.bin_str() << '\n';  // 0xc90000 - 00000000110010010000000011001001b
    }
    


  • Ist ja schön, das man es nicht tun darf. Es ist trotzdem die bessere Variante. Man muss halt testen ob es auf einer bestimmten Plattform das Ergebnis gibt, was man möchte. Besser als tausende Bitshifts mit sonst welchen Fehlerquellen zu programmieren. Es gibt sicher gute Gründe, warum das der Standard so macht, es ist aber trotzdem nicht praxisgerecht. Vlt. wäre mal Zeit eine weitere Art Union zu haben, die aus mehreren struchts mit Bitfields besteht und dann im Fall von gleicher Bitanzahl ein gleichbleibendes Ergebnis auf einer Plattform zu garantieren. Das wäre eine Anforderung, mit der ich als Anwender leben könnte.



  • @TGGC Unions, aliasing and type-punning in practice: what works and what does not? - Und der letzte Ansatz in der accepted answer ist genau der Grund warum man es bleiben lassen sollte.

    @TGGC sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    Besser als tausende Bitshifts mit sonst welchen Fehlerquellen zu programmieren.

    Sorry, aber was soll daran bitte übermäßig fehleranfällig sein?



  • @Swordfish
    war mein Fehler 😣
    hätte

    regL = (regHL & 0xFF);
    

    sein sollen.

    In den Anfängen des Projektes hatte ich schon die Bitshift- und Binäroperationen verwendet. Habe dann aber in einem Testprojekt mit Unions Versuche angestellt und fand es dann, eine sehr bequeme Art soetwas zu Programmieren.

    Wobei die ganzen Bitshifts usw. ja fast alle gleich sind, nur eben mit unterschiedlichen Variablen (CPU Register). Da könnte man sich viel Fehlerträchtige Tipparbeit durch das definieren von #define Makros bzw. Inline-Funktionen erleichtern.



  • @Netzschleicher sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    Da könnte man sich viel Fehlerträchtige Tipparbeit durch das definieren von #define Makros bzw. Inline-Funktionen erleichtern.

    Was habe ich denn oben vorgemacht?



  • @Swordfish sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    Sorry, aber was soll daran bitte übermäßig fehleranfällig sein?
    Ich redete nirgends vom übermässig. Aber fehleranfällig ist jeder Code, also werden zwangsweise auch hier Fehler auftreten. Mir fällt jetzt direkt kein Beispiel ein, aber ich bin sicher wenn du dir ein paar Sekunden Zeit nimmst, findest du selber was...

    Mir ist völlig klar, das den Compilerherstellern erlaubt ist hier irgendwas einzubauen und nachdem ich Update x einspiele passiert auf einmal nicht mehr was ich erwarte. Bevorzuge ich trotzdem gegenüber dem manuelle Geshifte.



  • @TGGC Du darfst das gerne bevorzugen. Bloss bitte empfehle es niemand anderem! Es gibt schon genug Leute die von sich aus kaputtes C++ schreiben, da muss man nicht noch nachhelfen.



  • @TGGC sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    Mir ist völlig klar, das den Compilerherstellern erlaubt ist hier irgendwas einzubauen und nachdem ich Update x einspiele passiert auf einmal nicht mehr was ich erwarte. Bevorzuge ich trotzdem gegenüber dem manuelle Geshifte.

    "Manuell" wäre auch nicht die beste Lösung. Aber gerade in C++ sind doch schöne leichtgewichtige Abstraktionen möglich, die beim Kompilieren in genau die Instruktionen zerfallen, die man gerne haben möchte.

    Ich bin sicher, dass man hierfür eine schicke Register-Klasse bauen könnte, die sich mindestens so schön verwenden lässt wie das Union-Konstrukt. Vielleicht mit sowas wie r.hi und r.lo-Proxy-Objekten. Meiner Erfahrung nach generieren Compiler bei sowas sehr schönen schlanken Code, wenn man es sauber implementiert. Auch wenn es auf den ersten Blick etwas umständlich aussieht.

    Den ganzen Code mit nackten Shifts und Bitweisen Operationen zuzupflastern wäre ohnehin schlechter Stil und ganz gewiss fehleranfällig.


  • Mod

    @hustbaer sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    @TGGC Du darfst das gerne bevorzugen. Bloss bitte empfehle es niemand anderem! Es gibt schon genug Leute die von sich aus kaputtes C++ schreiben, da muss man nicht noch nachhelfen.

    Doch, man sollte das hier schon empfehlen. GCC garantiert, dass das immer funktionieren wird. Nachdem man diese Garantie in der Dokumentation gründlich gelesen hat, spricht auch nichts dagegen, diese zu benutzen. Dazu sind Compilererweiterungen schließlich da. Hier hätte man nämlich möglicherweise echte Laufzeitvorteile dadurch. Ich bin mir nach der Beschreibung des OP nicht sicher, auf welcher Art von Prozessor mit welchem Befehlssatz der Code ausgeführt werden soll, aber schlimmstenfalls wird der Zugriff zu einem Bitshift werden, und bestenfalls zu etwas, wo man gar nicht rechnen muss.

    Das Geschrei auf der ganzen Welt wäre ziemlich laut, wenn GCC ankündigen würde, diese Garantie abzuschaffen, von daher würde man sicherlich sehr lange im Voraus mitbekommen, wenn das irgendwann einmal passieren sollte (Wird es aber nie, jede Wette!). Für Kompatibilität kann man die genaue Zugriffsart ja auch noch wegabstrahieren, so wie das vorgeschlagen wurde, und auf anderen Systemen die Bitshiftvariante einsetzen.

    Ich denke schon, dass das alles wichtige Punkte sind, die man diskutieren sollte, anstatt pauschal zu sagen "Nach strikter Standardauslegung undefiniert, daher Nein!".



  • @Finnegan
    Wenn man es nicht manuell machen soll, gibts also eine automatisierte Lösung? Diese ist im Compiler eingebaut und die nutze ich, die ist tausendfach getestet. Wer meint, kann das gern neu erfinden und damit seine Zeit verschwenden mit Implementation und Bugsuche. Ich empfehle es niemandem.

    Der Standard erlaubt das sich hier Verhalten beliebig ändert aber niemand will das wirklich, weder User hoch Compilerbauer. Daher hoffe ich wie gesagt, das man eine Formulierung im Standard findet, die zumindest begrenzt definiertes Verhalten beschreibt ohne das Performance darunter leiden muss.



  • Wie gesagt habe ich zu Beginn des Projektes auch mit Bitshift und Binär-Operationen angefangen. Aber ich fand das im weiteren Verlauf mit den Unions praktischer.

        value--;
        regAF.Flag_H = ((value & 0x0F) == 0x0F);    // H is set if borrow from bit 4, reset otherwise
        regAF.Flag_PV = (value == 0x7F);            // P/V is set if m was 80H before operation; reset otherwise
        regAF.Flag_S = (value & 0x80);              // S is set if result is negative; reset otherwise
        regAF.Flag_Z = (value == 0x00);             // Z is set if result is zero; reset otherwise
        regAF.Flag_N = true;
    

    ist doch etwas einfacher zu lesen als,

        value--;
        ((value & 0x0F) == 0x0F) ? (regF |= Flag_H) : (regF &= ~Flag_H);
        (value == 0x7F) ? (regF |= Flag_PV) : (regF &= ~Flag_PV);
        (value & 0x80) ? (regF |= Flag_S) : (regF &= ~Flag_S);
        (value) ? (regF &= ~Flag_Z) : (regF |= Flag_Z);
        regF |= Flag_N;
    

    was mich jedoch interessieren würde ist, welche Version der Compiler besser optimieren kann, also
    welchen Code der Prozessor dann besser ausführen kann.



  • @Netzschleicher sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    ist doch etwas einfacher zu lesen als,

    Hm, es zwingt doch ja auch niemand, mit Bit-Operationen herumzufummeln.

    Genausogut könntest du eine Klasse für die Flags erstellen, z.B. so:

    #include <stdint.h>
    
    union UFlags {
       uint8_t F;
       struct {
            bool Flag_C : 1;    // 'Carry' Flag
            bool Flag_N : 1;    // 'Negative' Flag
            bool Flag_PV : 1;   // 'Parity/Overflow' Flag
            bool  : 1;
            bool Flag_H : 1;    // 'Half-Carry' Flag
            bool  : 1;
            bool Flag_Z : 1;    // 'Zero' Flag
            bool Flag_S : 1;    // 'Sign' Flag
        };
    };
    
    class Flags {
    public:
        uint8_t F;
    public:
        bool carry() { return F & 1; }
        void set_carry(bool val) { F &= ~1; F |= val; }
        bool negative() { return F & 2; }
        void set_negative(bool val) { F &= ~2; F |= val << 1; }
        // usw
    };
    
    int main(int argc, char **) {
        Flags f;
        f.F = argc;
        f.set_negative(f.carry() ? 0 : 1);
    
        UFlags uf;
        uf.F = argc;
        uf.Flag_N = uf.Flag_C ? 0 : 1;
    
        // returne wahlweise f.F oder uf.F!
        return f.F;
    }
    

    was mich jedoch interessieren würde ist, welche Version der Compiler besser optimieren kann, also
    welchen Code der Prozessor dann besser ausführen kann.

    Wenn du dir obigen Code im Compiler Explorer mal anschaust, bleibt nicht viel übrig. Wenn du diese Klasse Flags durch deine Union ersetzt (returne uf.F), kommt (bis auf Vertauschung von rax/rdi) sogar derselbe Assembercode raus. (mit x8664 clang-7.0.0 -Wall -O3 getestet)



  • @SeppJ sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    GCC garantiert, dass das immer funktionieren wird.

    Dann sollte man das wohl eher im Compiler/IDE-Forum diskutieren und nicht in Standart-C++?



  • @SeppJ Das ist halt so ein scheiss Thema wo's IMO nicht wirklich ne gute Lösung dafür gibt. Also schon wenn Performance Wurst ist, da heisst die Lösung einfach memcpy. Easy.

    Ansonsten... doof.

    Was die Garantie von GCC angeht: ja, die wird wohl in den meisten Fällen reichen. Natürlich kann man sich trotz dieser Garantie in den Fuss schiessen, nämlich indem man Zeiger auf die Variablen in der Union in eine Funktion gibt die vom Union nix weiss. (Wobei ich davon ausgehe dass die Garantie von GCC dermassen eingeschränkt formuliert ist dass solche und ähnliche Konstrukte verboten sind.)

    Was aber IMO auch wichtig wäre bei dem Thema: gibt es ähnliche Garantien auch bei Clang, MSVC, Intel, XL C/C++ und was ich sonst noch an (mehr oder weniger) wichtigen Compilern vergessen habe?


    C und C++ bräuchten einfach mehr Library Funktionen für sowas. Also für so einfache Sachen wie auch für diverse Bit-Fummeleien die von den üblichen modernen CPUs unterstützt werden. Ein paar davon werden mehr oder weniger zuverlässig zu einem Befehl optimiert, aber der Grossteil bleibt unnötig komplizierter Code.

    Dies hier festzustellen (=rumzusudern) hilft natürlich niemandem, aber ich konnte es mir nicht verkneifen 🙂


  • Mod

    @hustbaer sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    Was die Garantie von GCC angeht: ja, die wird wohl in den meisten Fällen reichen. Natürlich kann man sich trotz dieser Garantie in den Fuss schiessen, nämlich indem man Zeiger auf die Variablen in der Union in eine Funktion gibt die vom Union nix weiss. (Wobei ich davon ausgehe dass die Garantie von GCC dermassen eingeschränkt formuliert ist dass solche und ähnliche Konstrukte verboten sind.)

    Exakt. Die Garantie lautet salopp gesagt: "Du darfst das machen, aber zeig da bloß nicht mit 'nem Pointer drauf!"



  • Ich verstehe aber immer noch nicht warum man unbedingt eine (unportable) union dafür brauchen würde. Immerhin geht es "nur" um 16-bit register mit einer Handvoll (Tschernobyl-Generation) Flags:

    #include <climits>
    #include <cassert>
    #include <cstdint>
    
    #if CHAR_BIT != 8
    #   error Kiss my a$$!
    #endif
    
    using word = std::uint16_t;
    using byte = char unsigned;  // kein std::uint8_t ... aliasing ...
    
    byte set_bit(byte &dst, unsigned bit, bool value = true)
    {
    	assert(bit < CHAR_BIT);
    	return dst &= ~(1 << bit) | (value << bit);
    }
    
    bool get_bit(byte src, unsigned bit)
    {
    	assert(bit < CHAR_BIT);
    	return (src >> bit) & 1;
    }
    
    class reg16_t
    {
    	word value;
    
    public:
    	byte &high  = reinterpret_cast<byte*>(&value)[1];  // wird wohl immer
    	byte &low   = reinterpret_cast<byte*>(&value)[0];  // gratis sein.
    
    	reg16_t(word value = 0) : value{ value } {}
    	reg16_t& operator=(word value) { this->value = value; return *this; }
    	operator word&() { return value; }
    
    	bool get_carry() const                  { return get_bit(low, 0); }
    	void set_carry(bool value = true)       { set_bit(low, 0, value); }
    
    	bool get_negative() const               { return get_bit(low, 1); }
    	void set_negative(bool value = true)    { set_bit(low, 1, value); }
    
    	bool get_parity() const                 { return get_bit(low, 2); }
    	void set_parity(bool value = true)      { set_bit(low, 2, value); }
    
    	bool get_half_carry() const             { return get_bit(low, 4); }
    	void set_half_carry(bool value = true)  { set_bit(low, 4, value); }
    
    	bool get_zero() const                   { return get_bit(low, 6); }
    	void set_zero(bool value = true)        { set_bit(low, 6, value); }
    
    	bool get_sign() const                   { return get_bit(low, 7); }
    	void set_sign(bool value = true)        { set_bit(low, 7, value); }
    };
    

    Einmal schreiben, einmal testen, happy sein.
    Fire and forget.


  • Mod

    Sich über Unions beschweren, aber dann gröbste strict aliasing Verletzungen begehen. Super.



  • @SeppJ sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    aber dann gröbste strict aliasing Verletzungen begehen.

    Wo?


  • Mod

    @Swordfish sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    @SeppJ sagte in Unions oder doch Bit-Shift und Bit-Operationen:

    aber dann gröbste strict aliasing Verletzungen begehen.

    Wo?

    high, low und operator word& sind gleichzeitig aktive Referenzen unter unterschiedlichem Typ auf das gleiche Objekt?

    edit: Oh, super, jetzt wirst du sagen, dass dein byte ein unsigned char ist und das deswegen nicht aliased. Als ob das Absicht gewesen wäre. Naja, noch mal Glück gehabt, aber trotzdem ein sehr schlechtes Vorbild, weil das nur unter diesen genau speziell gewählten (zufällig getroffenen?) Umständen ausnahmsweise erlaubt ist.


Anmelden zum Antworten