преобразуйте обратный порядок байтов в прямой порядок байтов в C [не используя, обеспечил func] [закрытый]

87
задан Shimmy 31 October 2014 в 04:29
поделиться

8 ответов

Предполагая, что вам нужно, это простой байтовый своп, попробуйте что-то вроде

преобразование Nensigned 16 бит:

swapped = (num>>8) | (num<<8);

32-битное преобразование без знака:

swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
                    ((num<<8)&0xff0000) | // move byte 1 to byte 2
                    ((num>>8)&0xff00) | // move byte 2 to byte 1
                    ((num<<24)&0xff000000); // byte 0 to byte 3

Это сводится к заказам байтов с позиций 1234-4321. Если ваш вход был 0xDeadbeef , 32-битный своп эндана может иметь вывод 0xefbeadde .

Код выше должен быть очищен макросами или, по крайней мере, постоянными вместо магических чисел, но, надеюсь, это поможет как

редактировать: как указал другой ответ, есть платформа, ОС и инструкция набор конкретных альтернатив, которые может быть намного быстрее, чем выше. В ядре Linux есть макросы (например, CPU_TO_BE32), которые хорошо обрабатывают эндин довольно красиво. Но эти альтернативы относятся к их средам. На практике Endianness лучше всего разбираться с использованием смеси доступных подходов

153
ответ дан 24 November 2019 в 07:38
поделиться

Вот довольно общая версия; я ее не компилировал, так что, вероятно, есть опечатки, но вы должны получить идею,

void SwapBytes(void *pv, size_t n)
{
    assert(n > 0);

    char *p = pv;
    size_t lo, hi;
    for(lo=0, hi=n-1; hi>lo; lo++, hi--)
    {
        char tmp=p[lo];
        p[lo] = p[hi];
        p[hi] = tmp;
    }
}
#define SWAP(x) SwapBytes(&x, sizeof(x));

NB: Это не оптимизировано для скорости или пространства. Она предназначена для того, чтобы быть понятной (легко отлаживаемой) и переносимой.

Update 2018-04-04 Добавлена функция assert() для ловушки недействительного регистра n == 0, как замечено комментатором @chux.

12
ответ дан 24 November 2019 в 07:38
поделиться

В шутку:


#include <stdio.h>

int main (int argc, char *argv[])
{
    size_t sizeofInt = sizeof (int);
    int i;

    union
    {
        int x;
        char c[sizeof (int)];
    } original, swapped;

    original.x = 0x12345678;

    for (i = 0; i < sizeofInt; i++)
        swapped.c[sizeofInt - i - 1] = original.c[i];

    fprintf (stderr, "%x\n", swapped.x);

    return 0;
}
5
ответ дан 24 November 2019 в 07:38
поделиться

Если вы работаете на процессоре X86 или X86_64, большой Endian является родным. Таким образом,

для 16-битных значений

unsigned short wBigE = value;
unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);

для 32-битных значений

unsigned int   iBigE = value;
unsigned int   iLittleE = ((iBigE & 0xFF) << 24)
                        | ((iBigE & 0xFF00) << 8)
                        | ((iBigE >> 8) & 0xFF00)
                        | (iBigE >> 24);

Это не является наиболее эффективным решением, если компилятор не распознает, что это манипулирование на уровне байта и генерирует код байта. Но это не зависит от каких-либо трюков макета памяти и может быть легко превращена в макрос.

-4
ответ дан 24 November 2019 в 07:38
поделиться

EDIT: Эта функция меняет только эндианность выровненных 16-битных слов. Функция, часто необходимая для кодировок UTF-16/UCS-2. EDIT END.

Если вы хотите изменить эндианность блока памяти, вы можете воспользоваться моим молниеносно быстрым подходом. Размер массива памяти должен быть кратен 8.

#include <stddef.h>
#include <limits.h>
#include <stdint.h>

void ChangeMemEndianness(uint64_t *mem, size_t size) 
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;

size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
  *mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}

Эта функция полезна для изменения эндианности файлов Unicode UCS-2/UTF-16.

1
ответ дан 24 November 2019 в 07:38
поделиться

Изменить: Это библиотечные функции. Следование им - это ручной способ сделать это.

Я совершенно ошеломлен количеством людей, не знающих о __ byteswap_ushort, __byteswap_ulong и __byteswap_uint64 .Конечно, они специфичны для Visual C ++, но они компилируются до восхитительного кода на архитектурах x86 / IA-64. :)

Здесь явное использование инструкции bswap , взятой с этой страницы . Обратите внимание, что внутренняя форма выше будет всегда быстрее, чем эта , я добавил ее только для того, чтобы дать ответ без библиотечной процедуры.

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}
6
ответ дан 24 November 2019 в 07:38
поделиться
#include <stdint.h>


//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val ) 
{
    return (val << 8) | (val >> 8 );
}

//! Byte swap short
int16_t swap_int16( int16_t val ) 
{
    return (val << 8) | ((val >> 8) & 0xFF);
}

//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
    val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | (val >> 16);
}

//! Byte swap int
int32_t swap_int32( int32_t val )
{
    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | ((val >> 16) & 0xFFFF);
}

Обновление : добавлена ​​перестановка 64-битных байтов

int64_t swap_int64( int64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}

uint64_t swap_uint64( uint64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | (val >> 32);
}
58
ответ дан 24 November 2019 в 07:38
поделиться

Будет ли это работать / быстрее?

 uint32_t swapped, result;

((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];
3
ответ дан 24 November 2019 в 07:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: