Вопрос по c++, endianness – Как преобразовать значения с прямым порядком байтов и байтов с обратным порядком байтов в C ++?

177

Как преобразовать значения с прямым порядком байтов и байтов с обратным порядком байтов в C ++?

РЕДАКТИРОВАТЬ: Для ясности, я должен переводить двоичные данные (значения с плавающей запятой двойной точности и 32-разрядные и 64-разрядные целые числа) из одной архитектуры ЦП в другую. Это не связано с сетью, поэтому ntoh () и подобные функции здесь не будут работать.

РЕДАКТИРОВАНИЕ № 2: Ответ, который я принял, относится непосредственно к компиляторам, на которые я нацеливаюсь (именно поэтому я выбрал его). Однако есть и другие очень хорошие, более портативные ответы.

Лучший способ справиться с порядком байтов в общем случае - убедиться, что код работает как на хостах с младшим, так и на старшем байтах. Если это работает, вы, вероятно, сделали это правильно. Предполагая, что вы используете x86 / be, это опасно для практики. jakobengblom2
hton ntoh не будет работать, если машина с прямым порядком байтов, потому что задающий вопрос явно хочет выполнить преобразование. fabspro
@ jakobengblom2 - единственный человек, который упомянул это. Почти во всех примерах на этой странице используются такие понятия, как & quot; swap & quot; байты вместо того, чтобы делать это независимо от основной последовательности. Если вы имеете дело с внешними форматами файлов (которые имеют четко определенный порядок байтов), то наиболее переносимым способом является обработка внешних данных как потока байтов и преобразование потока байтов в собственные целые и из них. Я съеживаюсь каждый раз, когда вижуshort swap(short x) код, так как он сломается, если вы перейдете на платформу с другим порядком байтов. Matthieu M имеет единственный правильный ответ ниже. Mark Lakata
Вы думаете о проблеме совершенно неправильно. Задача не в том, «как преобразовать значения с прямым порядком байтов и в значения с прямым порядком байтов». Задача состоит в том, «как преобразовать значения с плавающей запятой и целочисленные значения в определенном формате в собственный формат моей платформы». Если вы все сделаете правильно, нативный формат может быть с прямым порядком байтов, с прямым порядком байтов, смешанным порядком байтов или троичным для всех ваших забот кода. David Schwartz
Нет, он будет работать нормально, даже если он не имеет ничего общего с сетью. Ben Collins

Ваш Ответ

28   ответов
1

igEndian и LittleEndian

#define uint32_t unsigned 
#define uint16_t unsigned short

#define swap16(x) ((((uint16_t)(x) & 0x00ff)<<8)| \
(((uint16_t)(x) & 0xff00)>>8))

#define swap32(x) ((((uint32_t)(x) & 0x000000ff)<<24)| \
(((uint32_t)(x) & 0x0000ff00)<<8)| \
(((uint32_t)(x) & 0x00ff0000)>>8)| \
(((uint32_t)(x) & 0xff000000)>>24))
0

Недавно я написал макрос для этого в C, но он одинаково действителен в C ++:

#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)

Он принимает любой тип и переворачивает байты в переданном аргументе. Пример использования:

int main(){
    unsigned long long x = 0xABCDEF0123456789;
    printf("Before: %llX\n",x);
    REVERSE_BYTES(x);
    printf("After : %llX\n",x);

    char c[7]="nametag";
    printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
    REVERSE_BYTES(c);
    printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}

Какие отпечатки:

Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman

Вышесказанное идеально подходит для копирования / вставки, но здесь многое происходит, поэтому я расскажу, как это работает по частям:

Первая заметная вещь заключается в том, что весь макрос заключен вdo while(0) блок. Этообщая идиома чтобы разрешить нормальное использование точки с запятой после макроса.

Следующим шагом является использование переменной с именемREVERSE_BYTES какfor счетчик петли. Имя самого макроса используется в качестве имени переменной, чтобы гарантировать, что он не конфликтует с любыми другими символами, которые могут находиться в области видимости везде, где используется макрос. Поскольку имя используется в расширении макроса, оно не будет снова раскрыто при использовании в качестве имени переменной здесь.

В пределахfor цикл, есть два байта, на которые ссылаются иXOR поменялся местами (поэтому временное имя переменной не требуется):

((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]

__VA_ARGS__ представляет все, что было дано макросу, и используется для увеличения гибкости того, что может быть передано (хотя и ненамного). Адрес этого аргумента затем берется и приводитсяunsigned char указатель для разрешения обмена его байтов через массив[] индексирование.

Последний специфический момент - отсутствие{} брекеты. Они не нужны, потому что все шаги в каждом свопе объединены соператор запятой, делая им одно заявление.

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

13

ереходу от порядкового номера к порядку байтов.

Вот пример кода:

void swapByteOrder(unsigned short& us)
{
    us = (us >> 8) |
         (us << 8);
}

void swapByteOrder(unsigned int& ui)
{
    ui = (ui >> 24) |
         ((ui<<8) & 0x00FF0000) |
         ((ui>>8) & 0x0000FF00) |
         (ui << 24);
}

void swapByteOrder(unsigned long long& ull)
{
    ull = (ull >> 56) |
          ((ull<<40) & 0x00FF000000000000) |
          ((ull<<24) & 0x0000FF0000000000) |
          ((ull<<8) & 0x000000FF00000000) |
          ((ull>>8) & 0x00000000FF000000) |
          ((ull>>24) & 0x0000000000FF0000) |
          ((ull>>40) & 0x000000000000FF00) |
          (ull << 56);
}
Спасибо, что поймали это!
Последняя опубликованная здесь функция неверна и должна быть отредактирована следующим образом: void swapByteOrder (unsigned long long & amp; ull) {ull = (ull & gt; & gt; 56) | ... (ull & lt; & lt; 56); }
Я не считаю правильным использовать логическое и (& amp; & amp;), а не побитовое-и (& amp;). Согласно спецификации C ++, оба операнда неявно преобразуются в bool, а это не то, что вам нужно.
-4

Посмотрите вверх, немного сдвинув, так как это в основном все, что вам нужно сделать, чтобы поменяться с небольшим - & gt; Big Endian. Затем, в зависимости от размера бита, вы меняете способ сдвига бита.

2

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

 template<typename T>
    void ByteSwap(T * p)
    {
        for (int i = 0;  i < sizeof(T)/2;  ++i)
            std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]);
    }

Disclaimer: Я еще не пытался скомпилировать или протестировать его.

76

#include <climits>

template <typename T>
T swap_endian(T u)
{
    static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");

    union
    {
        T u;
        unsigned char u8[sizeof(T)];
    } source, dest;

    source.u = u;

    for (size_t k = 0; k < sizeof(T); k++)
        dest.u8[k] = source.u8[sizeof(T) - k - 1];

    return dest.u;
}

использование:swap_endian<uint32_t>(42).

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededthis question.
Error: User Rate Limit Exceededa char or unsigned char type.Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
3

дит как 0xAABBCCDD, равное 2864434397, то это же 32-разрядное целое число без знака выглядит как 0xDDCCBBAA на процессоре с прямым порядком байтов, который также равен 2864434397.

Если 16-разрядное короткое число без знака с прямым порядком байтов выглядит как 0xAABB, что равно 43707, то такое же короткое 16-разрядное без знака выглядит как 0xBBAA на процессоре с прямым порядком байтов, который также равен 43707.

Вот несколько удобных функций #define, позволяющих поменять байты с младшего к старшему и наоборот - & gt;

// can be used for short, unsigned short, word, unsigned word (2-byte types)
#define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8))

// can be used for int or unsigned int or float (4-byte types)
#define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16))

// can be used for unsigned long long or double (8-byte types)
#define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32))
65

ОтОшибка порядка байтов Роб Пайк:

Let's say your data stream has a little-endian-encoded 32-bit integer. Here's how to extract it (assuming unsigned bytes):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

If it's big-endian, here's how to extract it:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

TL;DR: Не беспокойтесь о собственном порядке вашей платформы, все, что имеет значение, это порядок байтов потока, из которого вы читаете, и вам лучше надеяться, что он четко определен.

Note: it was remarked in the comment that absent explicit type conversion, it was important that data be an array of unsigned char or uint8_t. Using signed char or char (if signed) will result in data[x] being promoted to an integer and data[x] << 24 potentially shifting a 1 into the sign bit which is UB.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceedednotError: User Rate Limit ExceededtheirError: User Rate Limit Exceeded
Error: User Rate Limit ExceedediError: User Rate Limit Exceeded
0

вненных не-байтовых методов доступа. Они работают на каждом компиляторе, каждом выравнивании границ и каждом порядке байтов. Эти невыровненные подпрограммы дополняются или обсуждаются в зависимости от собственного порядка байтов и выравнивания. Частичное перечисление, но вы поняли идею. BO * - это постоянные значения, основанные на собственном порядке байтов.

uint32_t sw_get_uint32_1234(pu32)
uint32_1234 *pu32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32_1234[0] = (*pu32)[BO32_0];
  bou32.u32_1234[1] = (*pu32)[BO32_1];
  bou32.u32_1234[2] = (*pu32)[BO32_2];
  bou32.u32_1234[3] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_1234(pu32, u32)
uint32_1234 *pu32;
uint32_t u32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_1234[0];
  (*pu32)[BO32_1] = bou32.u32_1234[1];
  (*pu32)[BO32_2] = bou32.u32_1234[2];
  (*pu32)[BO32_3] = bou32.u32_1234[3];
}

#if HAS_SW_INT64
int64 sw_get_int64_12345678(pi64)
int64_12345678 *pi64;
{
  union {
    int64_12345678 i64_12345678;
    int64 i64;
  } boi64;
  boi64.i64_12345678[0] = (*pi64)[BO64_0];
  boi64.i64_12345678[1] = (*pi64)[BO64_1];
  boi64.i64_12345678[2] = (*pi64)[BO64_2];
  boi64.i64_12345678[3] = (*pi64)[BO64_3];
  boi64.i64_12345678[4] = (*pi64)[BO64_4];
  boi64.i64_12345678[5] = (*pi64)[BO64_5];
  boi64.i64_12345678[6] = (*pi64)[BO64_6];
  boi64.i64_12345678[7] = (*pi64)[BO64_7];
  return(boi64.i64);
}
#endif

int32_t sw_get_int32_3412(pi32)
int32_3412 *pi32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32_3412[2] = (*pi32)[BO32_0];
  boi32.i32_3412[3] = (*pi32)[BO32_1];
  boi32.i32_3412[0] = (*pi32)[BO32_2];
  boi32.i32_3412[1] = (*pi32)[BO32_3];
  return(boi32.i32);
}

void sw_set_int32_3412(pi32, i32)
int32_3412 *pi32;
int32_t i32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32 = i32;
  (*pi32)[BO32_0] = boi32.i32_3412[2];
  (*pi32)[BO32_1] = boi32.i32_3412[3];
  (*pi32)[BO32_2] = boi32.i32_3412[0];
  (*pi32)[BO32_3] = boi32.i32_3412[1];
}

uint32_t sw_get_uint32_3412(pu32)
uint32_3412 *pu32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32_3412[2] = (*pu32)[BO32_0];
  bou32.u32_3412[3] = (*pu32)[BO32_1];
  bou32.u32_3412[0] = (*pu32)[BO32_2];
  bou32.u32_3412[1] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_3412(pu32, u32)
uint32_3412 *pu32;
uint32_t u32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_3412[2];
  (*pu32)[BO32_1] = bou32.u32_3412[3];
  (*pu32)[BO32_2] = bou32.u32_3412[0];
  (*pu32)[BO32_3] = bou32.u32_3412[1];
}

float sw_get_float_1234(pf)
float_1234 *pf;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f_1234[0] = (*pf)[BO32_0];
  bof.f_1234[1] = (*pf)[BO32_1];
  bof.f_1234[2] = (*pf)[BO32_2];
  bof.f_1234[3] = (*pf)[BO32_3];
  return(bof.f);
}

void sw_set_float_1234(pf, f)
float_1234 *pf;
float f;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f = (float)f;
  (*pf)[BO32_0] = bof.f_1234[0];
  (*pf)[BO32_1] = bof.f_1234[1];
  (*pf)[BO32_2] = bof.f_1234[2];
  (*pf)[BO32_3] = bof.f_1234[3];
}

double sw_get_double_12345678(pd)
double_12345678 *pd;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d_12345678[0] = (*pd)[BO64_0];
  bod.d_12345678[1] = (*pd)[BO64_1];
  bod.d_12345678[2] = (*pd)[BO64_2];
  bod.d_12345678[3] = (*pd)[BO64_3];
  bod.d_12345678[4] = (*pd)[BO64_4];
  bod.d_12345678[5] = (*pd)[BO64_5];
  bod.d_12345678[6] = (*pd)[BO64_6];
  bod.d_12345678[7] = (*pd)[BO64_7];
  return(bod.d);
}

void sw_set_double_12345678(pd, d)
double_12345678 *pd;
double d;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d = d;
  (*pd)[BO64_0] = bod.d_12345678[0];
  (*pd)[BO64_1] = bod.d_12345678[1];
  (*pd)[BO64_2] = bod.d_12345678[2];
  (*pd)[BO64_3] = bod.d_12345678[3];
  (*pd)[BO64_4] = bod.d_12345678[4];
  (*pd)[BO64_5] = bod.d_12345678[5];
  (*pd)[BO64_6] = bod.d_12345678[6];
  (*pd)[BO64_7] = bod.d_12345678[7];
}

Эти typedefs имеют то преимущество, что выдают ошибки компилятора, если они не используются с аксессорами, таким образом уменьшая забытые ошибки аксессора.

typedef char int8_1[1], uint8_1[1];

typedef char int16_12[2], uint16_12[2]; /* little endian */
typedef char int16_21[2], uint16_21[2]; /* big endian */

typedef char int24_321[3], uint24_321[3]; /* Alpha Micro, PDP-11 */

typedef char int32_1234[4], uint32_1234[4]; /* little endian */
typedef char int32_3412[4], uint32_3412[4]; /* Alpha Micro, PDP-11 */
typedef char int32_4321[4], uint32_4321[4]; /* big endian */

typedef char int64_12345678[8], uint64_12345678[8]; /* little endian */
typedef char int64_34128756[8], uint64_34128756[8]; /* Alpha Micro, PDP-11 */
typedef char int64_87654321[8], uint64_87654321[8]; /* big endian */

typedef char float_1234[4]; /* little endian */
typedef char float_3412[4]; /* Alpha Micro, PDP-11 */
typedef char float_4321[4]; /* big endian */

typedef char double_12345678[8]; /* little endian */
typedef char double_78563412[8]; /* Alpha Micro? */
typedef char double_87654321[8]; /* big endian */
Error: User Rate Limit Exceeded
4

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
Error: User Rate Limit Exceededchar[]Error: User Rate Limit Exceeded
11

// Specialization for 2-byte types.
template<>
inline void endian_byte_swapper< 2 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    ushort* p_dest = reinterpret_cast< ushort* >(dest);
    ushort const* const p_src = reinterpret_cast< ushort const* >(src);
    *p_dest = (*p_src >> 8) | (*p_src << 8);
}

// Specialization for 4-byte types.
template<>
inline void endian_byte_swapper< 4 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    uint* p_dest = reinterpret_cast< uint* >(dest);
    uint const* const p_src = reinterpret_cast< uint const* >(src);
    *p_dest = (*p_src >> 24) | ((*p_src & 0x00ff0000) >> 8) | ((*p_src & 0x0000ff00) << 8) | (*p_src << 24);
}
0

Ничего себе, я не могу поверить некоторым ответам, которые я прочитал здесь. На самом деле существует инструкция по сборке, которая делает это быстрее, чем что-либо еще. BSWAP. Вы могли бы просто написать такую функцию ...

__declspec(naked) uint32_t EndianSwap(uint32 value)
{
    __asm
    {
        mov eax, dword ptr[esp + 4]
        bswap eax
        ret
    }
}

этоMUCH быстрее, чем внутренности, которые были предложены. Я их разобрал и посмотрел. Вышеупомянутая функция не имеет пролога / эпилога, поэтому практически не имеет накладных расходов вообще.

unsigned long _byteswap_ulong(unsigned long value);

Выполнение 16-битной системы также просто, за исключением того, что вы используете xchg al, ах. bswap работает только на 32-битных регистрах.

64-битная версия немного сложнее, но не слишком. Гораздо лучше, чем все приведенные выше примеры с циклами, шаблонами и т. Д.

Здесь есть некоторые предостережения ... Во-первых, bswap доступен только для процессоров 80x486 и выше. Кто-нибудь планирует запустить его на 386?!? Если это так, вы все равно можете заменить bswap на ...

mov ebx, eax
shr ebx, 16
xchg bl, bh
xchg al, ah
shl eax, 16
or eax, ebx

Также встроенная сборка доступна только в коде x86 в Visual Studio. Обнаженная функция не может быть встроена, а также недоступна в сборках x64. В этом случае вам придется использовать встроенные функции компилятора.

_byteswap_ulongError: User Rate Limit Exceeded_uint64Error: User Rate Limit ExceededbswapError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededwm.ite.pl/articles/reverse-array-of-bytes.html
8

посмотрите на функции ntoh и hton.

6

ствует файл endian.h, который можно использовать для определения того, какую кодировку использует ваша система. Оттуда это примерно так:

unsigned int change_endian(unsinged int x)
{
    unsigned char *ptr = (unsigned char *)&x;
    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}

Это меняет порядок (от big-endian до little-endian):

Если у вас есть число 0xDEADBEEF (в системе с прямым порядком байтов, хранящейся как 0xEFBEADDE), ptr [0] будет 0xEF, ptr [1] будет 0xBE и т. Д.

Но если вы хотите использовать его для работы в сети, то htons, htonl и htonll (и их обратные ntohs, ntohl и ntohll) будут полезны для преобразования из порядка хостов в порядок сетей.

Это правда. Я исправлю это.
Ты можешь использоватьhtonlError: User Rate Limit Exceeded
Error: User Rate Limit Exceededopengroup.org/onlinepubs/9699919799/toc.htmError: User Rate Limit Exceeded
15

Существует инструкция по сборке под названием BSWAP, которая сделает замену,extremely fast. You can read about it Вот.

Visual Studio, или точнее библиотека времени выполнения Visual C ++, имеет встроенную платформу для этого, называемую_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(), Подобное должно существовать для других платформ, но я не знаю, как они будут называться.

Error: User Rate Limit Exceededgmplib.org/~tege/x86-timing.pdfError: User Rate Limit Exceededagner.org/optimize/instruction_tables.pdf
Error: User Rate Limit Exceeded
1

Я действительно удивлен, что никто не упомянул функции htobeXX и betohXX. Они определены в endian.h и очень похожи на сетевые функции htonXX.

25

чтобы сформировать это:

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>

enum endianness
{
    little_endian,
    big_endian,
    network_endian = big_endian,

    #if defined(BOOST_LITTLE_ENDIAN)
        host_endian = little_endian
    #elif defined(BOOST_BIG_ENDIAN)
        host_endian = big_endian
    #else
        #error "unable to determine system endianness"
    #endif
};

namespace detail {

template<typename T, size_t sz>
struct swap_bytes
{
    inline T operator()(T val)
    {
        throw std::out_of_range("data size");
    }
};

template<typename T>
struct swap_bytes<T, 1>
{
    inline T operator()(T val)
    {
        return val;
    }
};

template<typename T>
struct swap_bytes<T, 2>
{
    inline T operator()(T val)
    {
        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
    }
};

template<typename T>
struct swap_bytes<T, 4>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff000000) >> 24) |
                (((val) & 0x00ff0000) >>  8) |
                (((val) & 0x0000ff00) <<  8) |
                (((val) & 0x000000ff) << 24));
    }
};

template<>
struct swap_bytes<float, 4>
{
    inline float operator()(float val)
    {
        uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
        return *(float*)&mem;
    }
};

template<typename T>
struct swap_bytes<T, 8>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff00000000000000ull) >> 56) |
                (((val) & 0x00ff000000000000ull) >> 40) |
                (((val) & 0x0000ff0000000000ull) >> 24) |
                (((val) & 0x000000ff00000000ull) >> 8 ) |
                (((val) & 0x00000000ff000000ull) << 8 ) |
                (((val) & 0x0000000000ff0000ull) << 24) |
                (((val) & 0x000000000000ff00ull) << 40) |
                (((val) & 0x00000000000000ffull) << 56));
    }
};

template<>
struct swap_bytes<double, 8>
{
    inline double operator()(double val)
    {
        uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
        return *(double*)&mem;
    }
};

template<endianness from, endianness to, class T>
struct do_byte_swap
{
    inline T operator()(T value)
    {
        return swap_bytes<T, sizeof(T)>()(value);
    }
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian,    big_endian,    T> { inline T operator()(T value) { return value; } };

} // namespace detail

template<endianness from, endianness to, class T>
inline T byte_swap(T value)
{
    // ensure the data is only 1, 2, 4 or 8 bytes
    BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
    // ensure we're only swapping arithmetic types
    BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

    return detail::do_byte_swap<from, to, T>()(value);
}
Error: User Rate Limit Exceeded
5

Обратите внимание, что, по крайней мере, для Windows, htonl () намного медленнее, чем их внутренний аналог _byteswap_ulong (). Первый - это вызов библиотеки DLL в ws2_32.dll, второй - одна инструкция по сборке BSWAP. Поэтому, если вы пишете какой-то платформо-зависимый код, предпочитайте использовать встроенные функции для скорости:

#define htonl(x) _byteswap_ulong(x)

Это может быть особенно важно для обработки изображений .PNG, где все целые числа сохраняются в Big Endian с объяснением «Можно использовать htonl () ...» {чтобы замедлить типичные программы Windows, если вы не готовы}.

2

Если вы возьмете общий шаблон для изменения порядка битов в слове и выберете часть, которая инвертирует биты в каждом байте, то у вас останется нечто, что инвертирует только байты в слове. Для 64-битных:

x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff);
x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff);
x = ((x & 0x00ff00ff00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff00ff00ff);

Компиляторshould очистите лишние операции маскирования битов (я оставил их, чтобы выделить шаблон), но если это не так, вы можете переписать первую строку следующим образом:

x = ( x                       << 32) ^  (x >> 32);

Обычно это должно упростить до одной инструкции поворота на большинстве архитектур (игнорируя, что вся операция, вероятно, является одной инструкцией).

На процессоре RISC большие сложные константы могут вызывать трудности компиляции. Вы можете легко вычислить каждую из констант из предыдущей. Вот так:

uint64_t k = 0x00000000ffffffff; /* compiler should know a trick for this */
x = ((x & k) << 32) ^ ((x >> 32) & k);
k ^= k << 16;
x = ((x & k) << 16) ^ ((x >> 16) & k);
k ^= k << 8;
x = ((x & k) <<  8) ^ ((x >>  8) & k);

Если вам нравится, вы можете написать это в виде цикла. Это не будет эффективным, но просто для удовольствия:

int i = sizeof(x) * CHAR_BIT / 2;
uintmax_t k = (1 << i) - 1;
while (i >= 8)
{
    x = ((x & k) << i) ^ ((x >> i) & k);
    i >>= 1;
    k ^= k << i;
}

А для полноты приведем упрощенную 32-битную версию первой формы:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);
48

вы должны использовать:

ntohl() //Network to Host byte order (Long)
htonl() //Host to Network byte order (Long)

ntohs() //Network to Host byte order (Short)
htons() //Host to Network byte order (Short)

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededhtonlError: User Rate Limit ExceededntohlError: User Rate Limit Exceeded
3

У меня есть этот код, который позволяет мне конвертировать из HOST_ENDIAN_ORDER (что бы это ни было) в LITTLE_ENDIAN_ORDER или BIG_ENDIAN_ORDER. Я использую шаблон, поэтому, если я попытаюсь преобразовать из HOST_ENDIAN_ORDER в LITTLE_ENDIAN_ORDER, и они окажутся одинаковыми для машины, которую я компилирую, код не будет сгенерирован.

Вот код с некоторыми комментариями:

// We define some constant for little, big and host endianess. Here I use 
// BOOST_LITTLE_ENDIAN/BOOST_BIG_ENDIAN to check the host indianess. If you
// don't want to use boost you will have to modify this part a bit.
enum EEndian
{
  LITTLE_ENDIAN_ORDER,
  BIG_ENDIAN_ORDER,
#if defined(BOOST_LITTLE_ENDIAN)
  HOST_ENDIAN_ORDER = LITTLE_ENDIAN_ORDER
#elif defined(BOOST_BIG_ENDIAN)
  HOST_ENDIAN_ORDER = BIG_ENDIAN_ORDER
#else
#error "Impossible de determiner l'indianness du systeme cible."
#endif
};

// this function swap the bytes of values given it's size as a template
// parameter (could sizeof be used?).
template <class T, unsigned int size>
inline T SwapBytes(T value)
{
  union
  {
     T value;
     char bytes[size];
  } in, out;

  in.value = value;

  for (unsigned int i = 0; i < size / 2; ++i)
  {
     out.bytes[i] = in.bytes[size - 1 - i];
     out.bytes[size - 1 - i] = in.bytes[i];
  }

  return out.value;
}

// Here is the function you will use. Again there is two compile-time assertion
// that use the boost librarie. You could probably comment them out, but if you
// do be cautious not to use this function for anything else than integers
// types. This function need to be calles like this :
//
//     int x = someValue;
//     int i = EndianSwapBytes<HOST_ENDIAN_ORDER, BIG_ENDIAN_ORDER>(x);
//
template<EEndian from, EEndian to, class T>
inline T EndianSwapBytes(T value)
{
  // A : La donnée à swapper à une taille de 2, 4 ou 8 octets
  BOOST_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);

  // A : La donnée à swapper est d'un type arithmetic
  BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

  // Si from et to sont du même type on ne swap pas.
  if (from == to)
     return value;

  return SwapBytes<T, sizeof(T)>(value);
}
148

Visual C++ выполните следующее: вы включаете intrin.h и вызываете следующие функции:

Для 16-битных чисел:

unsigned short _byteswap_ushort(unsigned short value);

Для 32-битных чисел:

unsigned long _byteswap_ulong(unsigned long value);

Для 64-битных чисел:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8-битные числа (символы) не нужно преобразовывать.

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

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

Другие компиляторы также имеют аналогичные свойства.

ВGCC Например, вы можете напрямую позвонить:

int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)

(не нужно что-то включать). Afaik bits.h также объявляет ту же функцию не GCC-ориентированным способом.

16-битный обмен - это просто поворот.

Вызов intrinsics вместо того, чтобы катиться самостоятельно, дает вам лучшую производительность и плотность кода.

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededhtonlError: User Rate Limit ExceededntohlError: User Rate Limit ExceededhtonlError: User Rate Limit Exceededntohl.
Error: User Rate Limit Exceededhtonl, htonsError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
__builtin_bswapXError: User Rate Limit Exceeded
2

что добавил свое собственное решение здесь, так как нигде не видел его. Это небольшая и переносимая шаблонная функция C ++ и переносимая, которая использует только битовые операции.

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}
0

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

/*
* read a double from a stream in ieee754 format regardless of host
*  encoding.
*  fp - the stream
*  bigendian - set to if big bytes first, clear for little bytes
*              first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
    unsigned char buff[8];
    int i;
    double fnorm = 0.0;
    unsigned char temp;
    int sign;
    int exponent;
    double bitval;
    int maski, mask;
    int expbits = 11;
    int significandbits = 52;
    int shift;
    double answer;

    /* read the data */
    for (i = 0; i < 8; i++)
        buff[i] = fgetc(fp);
    /* just reverse if not big-endian*/
    if (!bigendian)
    {
        for (i = 0; i < 4; i++)
        {
            temp = buff[i];
            buff[i] = buff[8 - i - 1];
            buff[8 - i - 1] = temp;
        }
    }
    sign = buff[0] & 0x80 ? -1 : 1;
    /* exponet in raw format*/
    exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);

    /* read inthe mantissa. Top bit is 0.5, the successive bits half*/
    bitval = 0.5;
    maski = 1;
    mask = 0x08;
    for (i = 0; i < significandbits; i++)
    {
        if (buff[maski] & mask)
            fnorm += bitval;

        bitval /= 2.0;
        mask >>= 1;
        if (mask == 0)
        {
            mask = 0x80;
            maski++;
        }
    }
    /* handle zero specially */
    if (exponent == 0 && fnorm == 0)
        return 0.0;

    shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
    /* nans have exp 1024 and non-zero mantissa */
    if (shift == 1024 && fnorm != 0)
        return sqrt(-1.0);
    /*infinity*/
    if (shift == 1024 && fnorm == 0)
    {

#ifdef INFINITY
        return sign == 1 ? INFINITY : -INFINITY;
#endif
        return  (sign * 1.0) / 0.0;
    }
    if (shift > -1023)
    {
        answer = ldexp(fnorm + 1.0, shift);
        return answer * sign;
    }
    else
    {
        /* denormalised numbers */
        if (fnorm == 0.0)
            return 0.0;
        shift = -1022;
        while (fnorm < 1.0)
        {
            fnorm *= 2;
            shift--;
        }
        answer = ldexp(fnorm, shift);
        return answer * sign;
    }
}

Для остальной части набора функций, включая процедуры записи и целочисленного типа, смотрите мой проект на github.

https://github.com/MalcolmMcLean/ieee754

4

который обеспечивает эффективные функции byteswap. На Linux это в<endian.h>, Вы можете красиво обернуть это в C ++:

#include <iostream>

#include <endian.h>

template<size_t N> struct SizeT {};

#define BYTESWAPS(bits) \
template<class T> inline T htobe(T t, SizeT<bits / 8>) { return htobe ## bits(t); } \
template<class T> inline T htole(T t, SizeT<bits / 8>) { return htole ## bits(t); } \
template<class T> inline T betoh(T t, SizeT<bits / 8>) { return be ## bits ## toh(t); } \
template<class T> inline T letoh(T t, SizeT<bits / 8>) { return le ## bits ## toh(t); }

BYTESWAPS(16)
BYTESWAPS(32)
BYTESWAPS(64)

#undef BYTESWAPS

template<class T> inline T htobe(T t) { return htobe(t, SizeT<sizeof t>()); }
template<class T> inline T htole(T t) { return htole(t, SizeT<sizeof t>()); }
template<class T> inline T betoh(T t) { return betoh(t, SizeT<sizeof t>()); }
template<class T> inline T letoh(T t) { return letoh(t, SizeT<sizeof t>()); }

int main()
{
    std::cout << std::hex;
    std::cout << htobe(static_cast<unsigned short>(0xfeca)) << '\n';
    std::cout << htobe(0xafbeadde) << '\n';

    // Use ULL suffix to specify integer constant as unsigned long long 
    std::cout << htobe(0xfecaefbeafdeedfeULL) << '\n';
}

Выход:

cafe
deadbeaf
feeddeafbeefcafe
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
0

ПытатьсяBoost::endianИ НЕ РЕАЛИЗУЙТЕ ЕГО СЕБЯ!

Вот этоссылка на сайт

4

Серьезно ... Я не понимаю, почему все решения таковыcomplicated! How about the simplest, most general template function that swaps any type of any size under any circumstances in any operating system????

template <typename T>
void SwapEnd(T& var)
{
    char* varArray = reinterpret_cast<char*>(&var);
    for(long i = 0; i < static_cast<long>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
}

Это волшебная сила C и C ++ вместе! Просто поменяйте местами исходную переменную символ за символом.

Помните, что я не использовал простой оператор присваивания & quot; = & quot; потому что некоторые объекты будут испорчены, когда порядок байтов перевернут, и конструктор копирования (или оператор присваивания) не будет работать. Следовательно, более надежно копировать их char на char.

Чтобы позвонить, просто используйте

double x = 5;
SwapEnd(x);

и сейчасx отличается порядком байтов

Error: User Rate Limit Exceededfor(size_t i = 0 ; i < sizeof(var) ; i++)Error: User Rate Limit Exceededstatic_cast<long>Error: User Rate Limit Exceededchar*Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededMark Ransom's answerError: User Rate Limit Exceeded
Error: User Rate Limit Exceedednew/deleteError: User Rate Limit Exceededsizeof(var)Error: User Rate Limit Exceededchar varSwapped[sizeof(var)]Error: User Rate Limit Exceededchar *p = reinterpret_cast<char*>(&var)Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededstackoverflow.com/questions/36657895/…
6

short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));

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

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