Подтвердить что ты не робот

Преобразовать Маленький Эндиан в Большой Эндиан

Я просто хочу спросить, подходит ли мой метод для преобразования от маленького endian к большому endian, просто чтобы убедиться, что я понимаю разницу.

У меня есть число, которое хранится в little-endian, вот двоичные и шестнадцатеричные представления числа:

‭0001 0010 0011 0100 0101 0110 0111 1000‬

‭12345678‬

В формате big-endian я считаю, что байты должны быть заменены, например:

1000 0111 0110 0101 0100 0011 0010 0001

‭87654321

Правильно ли это?

Кроме того, код ниже пытается сделать это, но не работает. Есть что-то явно неправильное или я могу что-то оптимизировать? Если код плохо для этого преобразования, можете ли вы объяснить, почему и показать лучший способ выполнения одного и того же преобразования?

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);
4b9b3361

Ответ 1

Образец кода OP неправильный.

Преобразование Endian работает на уровне бит и 8 бит. Большинство вопросов, относящихся к концу, относятся к байтовому уровню. OP-код выполняет конечное изменение на уровне 4-битного уровня. Рекомендуем вместо этого:

// 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);

Если производительность действительно важна, конкретный процессор должен быть известен. В противном случае оставьте его компилятору.

[Edit] OP добавил комментарий, который меняет ситуацию.
"32-битное числовое значение, представленное шестнадцатеричным представлением (st uv wx yz), должно быть записано в четырехбайтовом поле как (st uv wx yz)."

В этом случае появляется endian 32-битного номера, и результат должен храниться в памяти в маленьком концевом порядке.

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 Изменить] Упрощение

... Тип результата - это продвинутый левый операнд.... Операторы побитового сдвига C11 §6.5.7 3

Используя u после того, как константы сдвига (правые операнды) приводят к тому же, что и без него.

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

Ответ 2

Я думаю, вы можете использовать функцию htonl(). Сетевой порядок байтов является большим.

Ответ 3

"Я правильно меняю байты?" → да, чтобы преобразовать между маленьким и большим endian, вы просто даете байтам противоположный порядок. Но сначала реализуйте несколько вещей:

  • размер uint32_t составляет 32 бит, что составляет 4 байта, что составляет 8 цифр HEX
  • mask 0xf извлекает 4 младших значащих бита, чтобы получить 8 бит, вам нужно 0xff

поэтому, если вы захотите поменять порядок в 4 байта с помощью таких масок, вы можете:

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 ;

Ответ 4

Вы можете сделать это:

int x = 0x12345678;

x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24)  ; 

printf("value = %x", x);  // x will be printed as 0x78563412

Ответ 5

Один из немногих способов решения этой проблемы, который иногда может быть полезен, состоит в объединении значения шестнадцати или тридцати двух бит и массива символов. Я только что делал это, когда получаю серийные сообщения, которые поступают с большим порядком, но я работаю над маленьким эндианским микро.

union MessageLengthUnion {

uint16_t asInt;
uint8_t asChars[2];

};

Затем, когда я получаю сообщения, я помещаю первый полученный uint8 в .asChars [1], второй в .asChars [0], затем я обращаюсь к нему как к .asInt части объединения в остальной части моей программы. Если у вас есть 32-битное значение для хранения, вы можете иметь массив длиной четыре.

Ответ 6

еще одно предложение:

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

Ответ 7

Я предполагаю, что вы используете linux

Включить "byteswap.h" и использовать int32_t bswap_32(int32_t argument);

Это логический вид. Фактически, /usr/include/byteswap.h

Ответ 8

#include<stdio.h>
int convert(int );
int main()
{
    unsigned int num,r;
    printf("enter the number :\n");
    scanf("%x",&num);
    r=convert(num);
    printf("after o/p = %x\n",r);
}
int convert(int num)
{
    int b0,b1,b2,b3;
    b0= (num & 0x000000FF)>>0;
    b1= (num & 0x0000FF00)>>8;
    b2= (num & 0x00FF0000)>>16;
    b3= (num & 0xFF000000)>>24;

    num= (b0<<24) | (b1<<16) | (b2<<8) | (b3<<0) ;

    return num;
}

Ответ 9

#include<stdio.h>
main()
{
    int num,i=0,temp;
    printf("enter the number\n");
    scanf("%d",&num);

    printf("before converting little to big binary is :\n");
    for(i=31;i>=0;i--)
    {
        printf("%d",num>>i&1);
        if(i%8==0)
            printf(" | ");
    }
    printf("\n");
    char *p=(char*)&num,*q=p+3;
    for(i=0;i<2;i++)
    {
        temp=*p;
        *p=*q;
        *q=temp;
        p++;
        q--;
    }
    printf("after converting little to big binary is :\n");
    for(i=31;i>=0;i--)
    {
        printf("%d",num>>i&1);
        if(i%8==0)
            printf(" | ");
    }
    printf("\n");
}

с помощью указателей мы можем легко сделать то же самое, что..

Ответ 10

Код OP неправильный по следующим причинам:

  • Сделки выполняются на границе полубайта (4 бит) вместо байтовой (8-разрядной) границы.
  • Операции с выключенным сдвигом << из последних четырех свопов неверны, они должны быть операциями shift-right >>, и их значения сдвига также должны быть исправлены.
  • Использование промежуточного хранилища не требуется, и поэтому код может быть переписан, чтобы быть более кратким/узнаваемым. При этом некоторые компиляторы смогут лучше оптимизировать код, распознавая часто используемый шаблон.

Рассмотрим следующий код, который эффективно преобразует значение без знака:

// Swap endian (big to little) or (little to big)
uint32_t num = 0x12345678;
uint32_t res =
    ((num & 0x000000FF) << 16) |
    ((num & 0x0000FF00) << 8) |
    ((num & 0x00FF0000) >> 8) |
    ((num & 0xFF000000) >> 16);

printf("%0x\n", res);

Результат представлен здесь как в двоичном, так и в шестнадцатеричном виде, обратите внимание на то, как байты менялись:

‭0111 1000 0101 0110 0011 0100 0001 0010‬

78563412

Оптимизация

Что касается производительности, оставьте его компилятору для оптимизации вашего кода, когда это возможно. Вы должны избегать ненужных структур данных, таких как массивы, для простых алгоритмов, подобных этому, поэтому обычно вызывают разные инструкции, такие как доступ к ОЗУ вместо использования регистров CPU.

Ответ 11

Вы можете использовать функции lib. Они сводятся к сборке, но если вы открыты для альтернативных реализаций на C, здесь они (предполагая, что int 32-бит):

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
}

И использование выполняется так:

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

Ответ 12

Простая программа для преобразования от маленького к большому

#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;
}