Word aus 2 Bytes basteln



  • Hey Ihr,

    Ich wollte nur eben fragen ob mein Code effektiver zu schreiben geht. Es sollen zwei Bytes in ein Word verwandelt werden die Bytes haben Big Endian format und das Word soll eben nach der Umwandlung auch Big Endian sein. Danke schonmal!

    LZSWORD MAKEWORD(LZSBYTE b_high_i, LZSBYTE b_low_i)
    {
    	LZSWORD w_retValue;
    	w_retValue = w_retValue ^ w_retValue;
    	w_retValue = w_retValue | b_high_i;
    	w_retValue = w_retValue >> 8;
    	w_retValue = w_retValue | b_low_i;
    	return w_retValue;
    }
    


  • m00ni schrieb:

    Hey Ihr,

    Ich wollte nur eben fragen ob mein Code effektiver zu schreiben geht. Es sollen zwei Bytes in ein Word verwandelt werden die Bytes haben Big Endian format und das Word soll eben nach der Umwandlung auch Big Endian sein. Danke schonmal!

    LZSWORD MAKEWORD(LZSBYTE b_high_i, LZSBYTE b_low_i)
    {
    	LZSWORD w_retValue;
    	w_retValue = w_retValue ^ w_retValue;
    	w_retValue = w_retValue | b_high_i;
    	w_retValue = w_retValue >> 8;
    	w_retValue = w_retValue | b_low_i;
    	return w_retValue;
    }
    

    Warum so compliziert? Das Ergebnis stimmt eh nicht. Hast Du das mal getestet?

    unsigned short MAKEWORD(unsigned char b_high_i, unsigned char b_low_i)
    {
       // für meine CPU
       unsigned short w_retNative = b_high_i << 8 | b_low_i;
    
       // anders rum
       unsigned short w_retEndian = b_low_i << 8 | b_high_i;
    
       // immer big
       unsigned short w_retBig;
       unsigned char *bigPtr = (unsigned char*)&w_retBig;
       *bigPtr++ = b_high_i;
       *bigPtr = b_low_i;
    
       // immer little
       unsigned short w_retLittle;
       unsigned char *littlePtr = (unsigned char*)&w_retLittle;
       *littlePtr++ = b_low_i;
       *littlePtr = b_high_i;
    
       ...
    }
    

    Die erste Zeile erzeugt ein Wort in der Endianess, die die aktuelle CPU nativ nutzt. Die zweite Zeile machts genau andersrum.

    [Edit: Noch zwei weitere Beispiele angehängt]

    mfg Martin



  • so?

    LZSWORD MAKEWORD(LZSBYTE b_high_i, LZSBYTE b_low_i) 
    { 
        return  (((LZSWORD) b_low_i) >> 8) | b_high_i;
    }
    

    bei big-endian kommt das höherwertigste Byte zuerst gespeichert.

    Btw:

    LZSWORD w_retValue;
    w_retValue = w_retValue ^ w_retValue;
    

    kann man einfacher durch

    LZSWORD w_retValue;
    w_retValue = 0;
    

    ersetzen.



  • Wow da hab ich ja einiges übersehen 😃 Danke für die super schnellen und hilfreichen Antworten.



  • supertux schrieb:

    so?

    ...(((LZSWORD) b_low_i) >> 8)...
    }
    

    Was passiert, wenn ich ein Byte um 8 Bit nach rechts shifte?



  • Hab mir das ganze jetzt nochmal angekuckt und wenn ich das nicht falsch verstehe ist Big Endian doch so aufgebaut:

    0....8....16

    Das heißt ich muss doch die least significant bits reinpacken und nach links verschieben dann einfach die oberen mit rein odern.

    LZSWORD w_retValue = b_low_i << 8 | b_high_i;

    Oder etwa nicht?

    bzw wenn ich das kürzer schreiben wollte:

    return ((LZSWORD) b_low_i << 😎 | b_high_i;

    Lieg ich da falsch und übersehe was?



  • m00ni schrieb:

    Hab mir das ganze jetzt nochmal angekuckt und wenn ich das nicht falsch verstehe ist Big Endian doch so aufgebaut:
    ...
    Lieg ich da falsch und übersehe was?

    Die Endianess einer CPU besagt ausschließlich wie die CPU ein Wort im Speicher ablegt. Big Endian heißt Most Significant Byte zu erst, Least Significant Byte zuletzt. Das macht sich für den Programmier dann und nur dann bemerkbar, wenn er ein Wort byte weise liest oder schreibt. Auch wenn er Assembler benutzt.

    wort << 8;  // verschiebe wort um 8 bit richtung höherwertiges byte
    

    macht immer das gleiche egal ob big oder little endian. Daher erzeugt mein erstes Beispiel immer das native format, das zweite immer das jeweils andere. Egal mit welchen Compiler das übersetzt und mit welcher CPU das dann ausgeführt wird. Oder anderes Beispiel

    wort = 1;
    wort << 8;
    printf( "%d\n", wort );
    

    ergibt immer 256.

    mfg Martin



  • Endianes wird auf Byteebene betrachtet und hat erstmal nichts mit Bits zu tun.

    Nimm das Wort 0x1234 an SPeicherstelle 1000. Dann steht bei BigEndian in 1000: 0x12 und in 1001: 0x34.
    Bei LittleEndian steht in 1000 die 0x34 und in 1001 die 0x12.

    Oder bei long 0x12345678 ->
    Lit : 1000: 0x78 0x56 0x34 0x12
    Big : 1000: 0x12 0x34 0x56 0x78



  • m00ni schrieb:

    die Bytes haben Big Endian format und das Word soll eben nach der Umwandlung auch Big Endian sein.

    Bytes selbst haben kein Endian, nur eine Reihung dieser.
    Da du Bigendian brauchst, kannst du den Unix-Standard POSIX benutzen, z.B.

    #include <arpa/inet.h> /* unter win auch winsock2.h */
    
    unsigned short bigend_word(unsigned char hi,unsigned char lo)
    {
      return htons(hi<<8|lo);
    }
    

    htons kümmert sich dann um den Rest.



  • mgaeckler schrieb:

    supertux schrieb:

    so?

    ...(((LZSWORD) b_low_i) >> 8)...
    }
    

    Was passiert, wenn ich ein Byte um 8 Bit nach rechts shifte?

    eigentlich solltest du dann 0 bekommen, deshalb habe ich es explizit auf einen Datentype gecastet, der mehr Bits enthält.

    m00ni schrieb:

    Hab mir das ganze jetzt nochmal angekuckt und wenn ich das nicht falsch verstehe ist Big Endian doch so aufgebaut:

    0....8....16

    Das heißt ich muss doch die least significant bits reinpacken und nach links verschieben dann einfach die oberen mit rein odern.

    LZSWORD w_retValue = b_low_i << 8 | b_high_i;

    Oder etwa nicht?

    ja, siehe meinen letzten Post. Wobei, wenn b_low_i nur 8-Bit breit ist, dann ist b_low_i << 8 sehr wahrscheinlich 0.



  • Ich haette 'nen union genommen.


  • Mod

    knivil schrieb:

    Ich haette 'nen union genommen.

    Strenggenommen aber undefiniertes Verhalten. Natürlich wird jede reale Implementierung das machen, was du hier erwartest, aber warum ohne Not Undefiniertes tun?



  • supertux schrieb:

    mgaeckler schrieb:

    supertux schrieb:

    so?

    ...(((LZSWORD) b_low_i) >> 8)...
    }
    

    Was passiert, wenn ich ein Byte um 8 Bit nach rechts shifte?

    eigentlich solltest du dann 0 bekommen, deshalb habe ich es explizit auf einen Datentype gecastet, der mehr Bits enthält.

    Wenn ich jetzt Dein Informatiklehrer wäre, würde ich sagen "Setzen 6". Bin ich aber nicht.
    1. Die Lösung mit 0 stimmt nur dann, wenn Du ein vorzeichenloses Byte hast. Bei einem vorzeichen behafteten Byte wird einfach das Vorzeichen nachgeschoben:

    -127 >> 8 = -1
    

    2. Meinst Du im Ernst, daß Dein cast aus Deinem 8 Bitwert einen 16 Bitwert zaubert? Pass auf: das passiert:
    10101010 gecastet wird zu 0000000010101010. So und jetzt stell Dir vor, das wird um 8 Bits nach rechts verschoben. Was haben wir dann?

    0000000000000000
    

    Dein cast hat also nichts, rein gar nichts gebracht. Bitte beachte, das Beispiel so gilt nur für vorzeichenlose Bytes.

    supertux schrieb:

    a, siehe meinen letzten Post. Wobei, wenn b_low_i nur 8-Bit breit ist, dann ist b_low_i << 8 sehr wahrscheinlich 0.

    Tut mir leid, schon wieder daneben. b_low_i wird automatisch zu einem int bzw. unsigned int gecastet. Es stehen damit 16 oder mehr Bits zur Verfügung und b_low_i << 8 wird daher dann und nur dann 0 wenn b_low_i schon 0 ist.

    mfg Martin



  • knivil schrieb:

    Ich haette 'nen union genommen.

    meinst Du sowas?

    union xyz
    {
       unsigned short  word;
       struct
       {
          unsigned char byte1, byte2;
       } endian;
    };
    

    Das ist nicht ungefährlich. Der Standard garantiert Dir nur, daß alle Datenfelder in einem struct in genau der Reihenfolge im Speicher erscheinen, wie sie auch in der Deklaration erscheinen. Der Compiler darf aber z.B. zwischen den Elementen beliebige Füllbytes einfügen.

    Der Union bringt Dir keinen nennenswerten Vorteil, daß es sich lohnt, dieses Risiko einzugehen.

    mfg Martin



  • Sicher, aber man kann ja mit sizeof nachpruefen. Aber da hier mit Endian argumentiert wird, haengt sowieso viel von aeusseren Umstaenden ab.

    Der Union bringt Dir keinen nennenswerten Vorteil, daß es sich lohnt, dieses Risiko einzugehen.

    Ich finde Bitschiften immer etwas verwirrend.



  • knivil schrieb:

    Sicher, aber man kann ja mit sizeof nachpruefen. Aber da hier mit Endian argumentiert wird, haengt sowieso viel von aeusseren Umstaenden ab.

    Der Union bringt Dir keinen nennenswerten Vorteil, daß es sich lohnt, dieses Risiko einzugehen.

    Ich finde Bitschiften immer etwas verwirrend.

    Ach ja? Und wie reagierst Du, wenn sizeof( word ) != sizeof( endian )?

    Was ist am Bitshift verwirrend? Grundregel: kümmere dich beim Bitshift nicht um die Endianess. Das macht der Prozessor für Dich. Einzig und allein die Vorzeichen sind wichtig.

    x << 1 ist das selbe wie x * 2
    x << 2 ist das selbe wie x * 4
    x << 3 ist das selbe wie x * 8

    u.s.w. u.s.f

    vieleicht hilft das, um die Bitshiftoperatoren zu verstehen.

    mfg Martin


  • Mod

    knivil schrieb:

    Sicher, aber man kann ja mit sizeof nachpruefen. Aber da hier mit Endian argumentiert wird, haengt sowieso viel von aeusseren Umstaenden ab.

    Der Union bringt Dir keinen nennenswerten Vorteil, daß es sich lohnt, dieses Risiko einzugehen.

    Ich finde Bitschiften immer etwas verwirrend.

    Aber die ganze Endianessgeschichte ist ohnehin unnötig, wenn man statt unions & Co lieber Bitshifterei benutzt.

    Und zu der obigen Castdiskussion: Denkt daran, dass die Datentypen während der Rechnung dank der integral promotion ohnehin vergrößert werden.



  • Wie portabel ist eigentlich dieser Code?

    struct Flag
    {
    	unsigned char f1 : 1;
    	unsigned char f2 : 1;
    	unsigned char f3 : 1;
    	unsigned char f4 : 1;
    	unsigned char f5 : 1;
    	unsigned char f6 : 1;
    	unsigned char f7 : 1;
    	unsigned char f8 : 1;
    };
    

    Nehmen wir an ich würde ein unsigned char über das Netzwerk verschicken. Wäre das portabel? Ist das struct portabel?


  • Mod

    Der interne Aufbau von bit-fields ist meines Wissens nach implementation defined, ich mag aber gerade nicht die entsprechende Stelle im Standard suchen und zitiere daher bloß aus dem Gedächtnis.



  • Und wenn ich einen unsigned char nehme und ihn manuell mit Bit Manipulationen manipuliere und über das Netzwerk versende. Wäre das portabel?


Anmelden zum Antworten