Little Endian converteren naar Big Endian

Ik wil alleen vragen of mijn methode correct is om van little endian naar big endian te converteren, om er zeker van te zijn of ik het verschil begrijp.

Ik heb een getal dat is opgeslagen in little-endian, hier zijn de binaire en hex-representaties van het getal:

‭0001 0010 0011 0100 0101 0110 0111 1000‬
‭12345678‬

In big-endian formaat geloof ik dat de bytes moeten worden verwisseld, zoals dit:

1000 0111 0110 0101 0100 0011 0010 0001
‭87654321

Is dit correct?

Ook de onderstaande code probeert dit te doen, maar faalt. Is er iets duidelijk mis of kan ik iets optimaliseren? Als de code slecht is voor deze conversie, kunt u dan uitleggen waarom en een betere methode laten zien om dezelfde conversie uit te voeren?

uint32_t num = 0x12345678;
uint32_t b0,b1,b2,b3,b4,b5,b6,b7;
uint32_t res = 0;
b0 = (num & 0xf) << 28;
b1 = (num & 0xf0) << 24;
b2 = (num & 0xf00) << 20;
b3 = (num & 0xf000) << 16;
b4 = (num & 0xf0000) << 12;
b5 = (num & 0xf00000) << 8;
b6 = (num & 0xf000000) << 4;
b7 = (num & 0xf0000000) << 4;
res = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7;
printf("%d\n", res);

Antwoord 1, autoriteit 100%

OP’s voorbeeldcode is onjuist.

Endian-conversie werkt op bit- en 8-bits byteniveau. De meeste endian-problemen hebben betrekking op het byteniveau. OP-code voert een endian-verandering uit op het 4-bits nibble-niveau. In plaats daarvan aanbevelen:

// Swap endian (big to little) or (little to big)
uint32_t num = 9;
uint32_t b0,b1,b2,b3;
uint32_t res;
b0 = (num & 0x000000ff) << 24u;
b1 = (num & 0x0000ff00) << 8u;
b2 = (num & 0x00ff0000) >> 8u;
b3 = (num & 0xff000000) >> 24u;
res = b0 | b1 | b2 | b3;
printf("%" PRIX32 "\n", res);

Als prestaties echt belangrijk zijn, moet de specifieke processor bekend zijn. Laat het anders aan de compiler over.

[Bewerken] OP heeft een opmerking toegevoegd die dingen verandert.
“De 32-bits numerieke waarde die wordt vertegenwoordigd door de hexadecimale weergave (st uv wx yz) wordt opgenomen in een veld van vier bytes als (st uv wx yz).”

In dit geval lijkt de endian van het 32-bits getal onbekenden moet het resultaat in kleineendian-volgorde in het geheugen worden opgeslagen.

>

uint32_t num = 9;
uint8_t b[4];
b[0] = (uint8_t) (num >>  0u);
b[1] = (uint8_t) (num >>  8u);
b[2] = (uint8_t) (num >> 16u);
b[3] = (uint8_t) (num >> 24u);

[2016 Bewerken] Vereenvoudiging

… Het type resultaat is dat van de gepromoveerde linker operand…. Bitsgewijze shift-operators C11 §6.5.7 3

Het gebruik van een una de shiftconstanten (rechter operanden) resulteert in hetzelfde als zonder.

b3 = (num & 0xff000000) >> 24u;
b[3] = (uint8_t) (num >> 24u);
// same as 
b3 = (num & 0xff000000) >> 24;
b[3] = (uint8_t) (num >> 24);

Antwoord 2, autoriteit 71%

Ik denk dat je de functie htonl()kunt gebruiken. Netwerkbytevolgorde is big endian.


Antwoord 3, autoriteit 66%

Sorry, mijn antwoord is een beetje te laat, maar het lijkt erop dat niemand de ingebouwde functies noemde om de bytevolgorde om te keren, wat erg belangrijk is in termen van prestaties.

De meeste moderne processors zijn little-endian, terwijl alle netwerkprotocollen big-endian zijn. Dat is geschiedenis en meer daarover kunt u vinden op Wikipedia.Maar dat betekent dat onze processors tussen kleine – en miljoenen keren big-endian terwijl we op internet surfen.

Daarom hebben de meeste architecturen speciale processorinstructies om deze taak te vergemakkelijken. Voor x86-architecturen is er BSWAP-instructie en voor ARM’s is er REV. Dit is de meest efficiënte manier om de bytevolgorde om te keren.

Om assemblage in onze C-code te voorkomen, kunnen we in plaats daarvan ingebouwde functies gebruiken. Voor GCC is er de functie __builtin_bswap32()en voor Visual C++ is er _byteswap_ulong(). Die functie zal op de meeste architecturen slechts één processorinstructiegenereren.

Hier is een voorbeeld:

#include <stdio.h>
#include <inttypes.h>
int main()
{
    uint32_t le = 0x12345678;
    uint32_t be = __builtin_bswap32(le);
    printf("Little-endian: 0x%" PRIx32 "\n", le);
    printf("Big-endian:    0x%" PRIx32 "\n", be);
    return 0;
}

Dit is de output die het produceert:

Little-endian: 0x12345678
Big-endian:    0x78563412

En hier is de demontage (zonder optimalisatie, d.w.z. -O0):

       uint32_t be = __builtin_bswap32(le);
   0x0000000000400535 <+15>:    mov    -0x8(%rbp),%eax
   0x0000000000400538 <+18>:    bswap  %eax
   0x000000000040053a <+20>:    mov    %eax,-0x4(%rbp)

Er is inderdaad maar één BSWAPinstructie.

Dus, als we de prestatiesbelangrijk vinden, moeten we die ingebouwde functies gebruiken in plaatsvan elke andere methode van byte-omkering. Gewoon mijn 2 cent.


Antwoord 4, autoriteit 20%

“Ik verwissel alle bytes toch?”-> ja, om te converteren tussen little en big endian, geef je de bytes gewoon de tegenovergestelde volgorde.
Maar realiseer je eerst een paar dingen:

  • grootte van uint32_tis 32 bits, wat 4 bytes is, wat 8 HEX-cijfers is
  • mask 0xfhaalt de 4 minst significante bits op, om 8 bits op te halen heb je 0xff
  • nodig

dus als je de volgorde van 4 bytes wilt omwisselen met dat soort maskers, kun je:

uint32_t res = 0;
b0 = (num & 0xff) << 24;        ; least significant to most significant
b1 = (num & 0xff00) << 8;       ; 2nd least sig. to 2nd most sig.
b2 = (num & 0xff0000) >> 8;     ; 2nd most sig. to 2nd least sig.
b3 = (num & 0xff000000) >> 24;  ; most sig. to least sig.
res = b0 | b1 | b2 | b3 ;

Antwoord 5, autoriteit 10%

Je zou dit kunnen doen:

int x = 0x12345678;
x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24)  ; 
printf("value = %x", x);  // x will be printed as 0x78563412

Antwoord 6, autoriteit 7%

Een enigszins andere manier om dit aan te pakken, die soms handig kan zijn, is om een unie te hebben van de waarde van zestien of tweeëndertig bits en een array van tekens. Ik heb dit net gedaan bij het ontvangen van seriële berichten die binnenkomen met big endian order, maar ik werk aan een little endian micro.

union MessageLengthUnion
{
    uint16_t asInt;
    uint8_t asChars[2];
};

Als ik de berichten binnenkrijg, plaats ik de eerste ontvangen uint8 in .asChars[1], de tweede in .asChars[0] en open ik het als het .asInt-gedeelte van de unie in de rest van mijn programma.

Als u een waarde van tweeëndertig bits wilt opslaan, kunt u de array vier lang hebben.


Antwoord 7, autoriteit 5%

Ik neem aan dat je linux gebruikt

Vermeld "byteswap.h"& Gebruik int32_t bswap_32(int32_t argument);

Het is een logische weergave, in feite zie, /usr/include/byteswap.h


Antwoord 8, autoriteit 2%

nog een suggestie:

unsigned int a = 0xABCDEF23;
a = ((a&(0x0000FFFF)) << 16) | ((a&(0xFFFF0000)) >> 16);
a = ((a&(0x00FF00FF)) << 8) | ((a&(0xFF00FF00)) >>8);
printf("%0x\n",a);

Antwoord 9, autoriteit 2%

Een eenvoudig C-programma om van klein naar groot te converteren

#include <stdio.h>
int main() {
unsigned int little=0x1234ABCD,big=0;
unsigned char tmp=0,l;
printf(" Little endian little=%x\n",little);
for(l=0;l < 4;l++) 
{
    tmp=0;
    tmp = little | tmp;
    big = tmp | (big << 8);
    little = little >> 8;
}
printf(" Big endian big=%x\n",big);
return 0;
}

10

U kunt de lib-functies gebruiken. Ze koken tot montage, maar als je open bent voor alternatieve implementaties in C, hier zijn ze (aangenomen dat Int 32-bits is):

void byte_swap16(unsigned short int *pVal16) {
//#define method_one 1
// #define method_two 1
#define method_three 1
#ifdef method_one
    unsigned char *pByte;
    pByte = (unsigned char *) pVal16;
    *pVal16 = (pByte[0] << 8) | pByte[1];
#endif
#ifdef method_two
    unsigned char *pByte0;
    unsigned char *pByte1;
    pByte0 = (unsigned char *) pVal16;
    pByte1 = pByte0 + 1;
    *pByte0 = *pByte0 ^ *pByte1;
    *pByte1 = *pByte0 ^ *pByte1;
    *pByte0 = *pByte0 ^ *pByte1;
#endif
#ifdef method_three
    unsigned char *pByte;
    pByte = (unsigned char *) pVal16;
    pByte[0] = pByte[0] ^ pByte[1];
    pByte[1] = pByte[0] ^ pByte[1];
    pByte[0] = pByte[0] ^ pByte[1];
#endif
}
void byte_swap32(unsigned int *pVal32) {
#ifdef method_one
    unsigned char *pByte;
    // 0x1234 5678 --> 0x7856 3412  
    pByte = (unsigned char *) pVal32;
    *pVal32 = ( pByte[0] << 24 ) | (pByte[1] << 16) | (pByte[2] << 8) | ( pByte[3] );
#endif
#if defined(method_two) || defined (method_three)
    unsigned char *pByte;
    pByte = (unsigned char *) pVal32;
    // move lsb to msb
    pByte[0] = pByte[0] ^ pByte[3];
    pByte[3] = pByte[0] ^ pByte[3];
    pByte[0] = pByte[0] ^ pByte[3];
    // move lsb to msb
    pByte[1] = pByte[1] ^ pByte[2];
    pByte[2] = pByte[1] ^ pByte[2];
    pByte[1] = pByte[1] ^ pByte[2];
#endif
}

en het gebruik wordt uitgevoerd zoals SO:

unsigned short int u16Val = 0x1234;
byte_swap16(&u16Val);
unsigned int u32Val = 0x12345678;
byte_swap32(&u32Val);

11

Hieronder is een andere aanpak die nuttig voor mij was

convertLittleEndianByteArrayToBigEndianByteArray (byte littlendianByte[], byte bigEndianByte[], int ArraySize){
    int i =0;
    for(i =0;i<ArraySize;i++){
      bigEndianByte[i] = (littlendianByte[ArraySize-i-1] << 7 & 0x80) | (littlendianByte[ArraySize-i-1] << 5 & 0x40) |
                            (littlendianByte[ArraySize-i-1] << 3 & 0x20) | (littlendianByte[ArraySize-i-1] << 1 & 0x10) |
                            (littlendianByte[ArraySize-i-1] >>1 & 0x08) | (littlendianByte[ArraySize-i-1] >> 3 & 0x04) |
                            (littlendianByte[ArraySize-i-1] >>5 & 0x02) | (littlendianByte[ArraySize-i-1] >> 7 & 0x01) ;
    }
}

12

Hieronder het programma produceer het resultaat als dat nodig is:

#include <stdio.h>
unsigned int Little_To_Big_Endian(unsigned int num);
int main( )
{
    int num = 0x11223344 ;
    printf("\n Little_Endian = 0x%X\n",num);
    printf("\n Big_Endian    = 0x%X\n",Little_To_Big_Endian(num));
}
unsigned int Little_To_Big_Endian(unsigned int num)
{
    return (((num >> 24) & 0x000000ff) | ((num >> 8) & 0x0000ff00) | ((num << 8) & 0x00ff0000) | ((num << 24) & 0xff000000));
}

En ook onderstaande functie kan worden gebruikt:

   unsigned int Little_To_Big_Endian(unsigned int num)
    {
        return (((num & 0x000000ff) << 24) | ((num & 0x0000ff00) << 8 ) | ((num & 0x00ff0000) >> 8) | ((num & 0xff000000) >> 24 ));
    }

13

#include<stdio.h>
int main(){
        int var = 0X12345678;
        var =  ((0X000000FF & var)<<24)|
               ((0X0000FF00 & var)<<8) |
               ((0X00FF0000 & var)>>8) |
               ((0XFF000000 & var)>>24);
        printf("%x",var);
}

Other episodes