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

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

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

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

28 ответов


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

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

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

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

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

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

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

16 бит своп-это просто бит-поворот.

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


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

#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-разрядное целое число с кодировкой little-endian. Вот как его извлечь (при условии, что байты без знака):

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

если это big-endian, вот как его извлечь:

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

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

Если у вас есть номер 0xDEADBEEF (в маленькой системе endian, хранящейся как 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, где все целые числа сохраняются в большом Endian с объяснением " можно использовать 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++ вместе! Просто замените исходный символ переменной на символ.

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

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

double x = 5;
SwapEnd(x);

и теперь x отличается в endianness.


У меня есть этот код, который позволяет мне конвертировать из 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 функции для обмена байтами с little-endian на big-endian и наоборот -->

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

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

на процессоре 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++ templated и portable, которая использует только битовые операции.

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. Они определены в endian.h и очень похожи на сетевые функции htonXX.


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

__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. Функция naked не может быть выровнена, а также недоступна в сборках x64. В этом случае вам придется использовать встроенные компоненты компилятора.


портативная техника для реализации оптимизатор-дружественных несогласованных не-inplace endian accessors. Они работают над каждым компилятором, каждым выравниванием границ и каждым порядком байтов. Эти несогласованные процедуры дополняются или обсуждаются в зависимости от родного endian и выравнивания. Частичный список, но вы поняли идею. 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 */

недавно я написал макрос, чтобы сделать это на 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 указатель для разрешения замены его байтов через array [] индексации.

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

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


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

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

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

https://github.com/MalcolmMcLean/ieee754


попробовать Boost::endian, и не реализуйте его самостоятельно!

здесь ссылке


посмотрите бит сдвига, так как это в основном все, что вам нужно сделать, чтобы поменять с little -> big endian. Затем в зависимости от размера бита вы меняете способ смещения бита.