Как мне преобразовать значения big-endian в little-endian в C ++?

StackOverflow https://stackoverflow.com/questions/105252

  •  01-07-2019
  •  | 
  •  

Вопрос

Как мне преобразовать значения big-endian в little-endian в C ++?

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

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

Это было полезно?

Решение

Если вы используете 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-битные числа (chars) не нуждаются в преобразовании.

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

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

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

В ССАГПЗ например, вы можете напрямую позвонить:

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

(нет необходимости что-то включать).Кусочки Афаика.h также объявляет ту же функцию не ориентированным на gcc способом.

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

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

Другие советы

Проще говоря:

#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).

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

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

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

Если это двоичный код, вот как его извлечь:

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

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

Примечание:в комментарии было отмечено, что при отсутствии явного преобразования типов важно, чтобы data быть массивом unsigned char или uint8_t.Используя signed char или char (если подписано) приведет к data[x] повышается до целого числа и data[x] << 24 потенциально смещая 1 в знаковый бит, которым является UB.

Если вы делаете это в целях обеспечения совместимости сети и хоста, вам следует использовать:

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 будет работать просто отлично.

Я взял несколько предложений из этого поста и объединил их, чтобы сформировать это:

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

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

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

Процедура перехода от биг-энда к биг-энду такая же, как и при переходе от биг-энда к биг-энду.

Вот несколько примеров кода:

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

Мы сделали это с помощью шаблонов.Вы могли бы сделать что-то вроде этого:

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

Если вы делаете это для передачи данных между разными платформами, обратите внимание на функции ntoh и hton.

Так же, как вы делаете в C:

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

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

В большинстве систем POSIX (поскольку этого нет в стандарте POSIX) есть 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];
}

Это меняет порядок (с большого на маленький).:

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

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

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

#define htonl(x) _byteswap_ulong(x)

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

Большинство платформ имеют системный заголовочный файл, который обеспечивает эффективные функции 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

мне нравится этот, просто из-за стиля :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}

Серьезно...Я не понимаю, почему все решения таковы сложный! Как насчет простейшей, наиболее общей шаблонной функции, которая заменяет любой тип любого размера при любых обстоятельствах в любой операционной системе????

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 ++ вместе взятых!Просто меняйте местами исходную переменную символ за символом.

Помните, что я не использовал простой оператор присваивания "=", потому что некоторые объекты будут перепутаны при изменении порядка следования и конструктор копирования (или оператор присваивания) не будет работать.Поэтому надежнее копировать их символ за символом.

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

double x = 5;
SwapEnd(x);

и теперь x отличается по порядку следования.

У меня есть этот код, который позволяет мне конвертировать из 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);
}

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

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

Вот пара удобных функций #define для замены байтов с младшего на старший и наоборот ->

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

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

 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]);
    }

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

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

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

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

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

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

На 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);

Просто подумал, что добавил сюда свое собственное решение, так как я его нигде не видел.Это небольшая и переносимая шаблонная функция на 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;
}

С помощью приведенных ниже кодов вы можете легко переключаться между BigEndian и 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))

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

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

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

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

unsigned long _byteswap_ulong(unsigned long value);

Выполнить 16-битную версию так же просто, за исключением того, что вы будете использовать xchg al, ah.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.В этом случае вам придется использовать встроенные функции компилятора.

Переносимый метод для реализации удобных для оптимизатора невыровненных не на месте конечных средств доступа.Они работают с каждым компилятором, с каждым выравниванием границ и с каждым порядком байтов.Эти невыровненные процедуры дополняются или обсуждаются в зависимости от собственного конечного символа и выравнивания.Неполный список, но идею вы уловили.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];
}

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

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 */

Недавно я написал макрос для этого на 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 цикл, на который ссылаются два байта и КСОР поменялся местами (таким образом, имя временной переменной не требуется):

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

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

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

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

Попробуй Boost::endian, и НЕ РЕАЛИЗУЙТЕ ЭТО САМОСТОЯТЕЛЬНО!

Вот такой Ссылка

Вот как прочитать double, сохраненный в 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

Замена байтов с помощью старого 3-шагового метода xor вокруг поворота в функции шаблона дает гибкое, быстрое решение O (ln2), которое не требует библиотеки, стиль здесь также отвергает типы размером в 1 байт:

template<typename T>void swap(T &t){
    for(uint8_t pivot = 0; pivot < sizeof(t)/2; pivot ++){
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
        *((uint8_t *)&t+sizeof(t)-1- pivot) ^= *((uint8_t *)&t + pivot);
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
    }
}

Похоже, что безопасным способом было бы использовать htons для каждого слова.Итак, если у вас есть...

std::vector<uint16_t> storage(n);  // where n is the number to be converted

// the following would do the trick
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });

Вышесказанное было бы недопустимым, если бы вы работали в системе big-endian, поэтому я бы поискал все, что ваша платформа использует в качестве условия времени компиляции, чтобы решить, является ли htons недопустимым.В конце концов, это O (n).На компьютере Mac это было бы что-то вроде ...

#if (__DARWIN_BYTE_ORDER != __DARWIN_BIG_ENDIAN)
std::transform(word_storage.cbegin(), word_storage.cend()
  , word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });
#endif

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top