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

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

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

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

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

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

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

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

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

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

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

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

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

 #include  template  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(42) .

От байт-ордера «Ошибка » Роба Пике:

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

 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: не беспокойтесь о своем собственном заказе на платформе, все, что считается, - это порядок байтов streamа, который вы читаете, и вам лучше надеяться, что он будет хорошо определен.

Примечание: в комментарии было отмечено, что при отсутствии явного преобразования типов важно, чтобы 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  #include  #include  #include  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 struct swap_bytes { inline T operator()(T val) { throw std::out_of_range("data size"); } }; template struct swap_bytes { inline T operator()(T val) { return val; } }; template struct swap_bytes { inline T operator()(T val) { return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8)); } }; template struct swap_bytes { inline T operator()(T val) { return ((((val) & 0xff000000) >> 24) | (((val) & 0x00ff0000) >> 8) | (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24)); } }; template<> struct swap_bytes { inline float operator()(float val) { uint32_t mem =swap_bytes()(*(uint32_t*)&val); return *(float*)&mem; } }; template struct swap_bytes { 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 { inline double operator()(double val) { uint64_t mem =swap_bytes()(*(uint64_t*)&val); return *(double*)&mem; } }; template struct do_byte_swap { inline T operator()(T value) { return swap_bytes()(value); } }; // specialisations when attempting to swap to the same endianess template struct do_byte_swap { inline T operator()(T value) { return value; } }; template struct do_byte_swap { inline T operator()(T value) { return value; } }; } // namespace detail template 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::value); return detail::do_byte_swap()(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)); 

Вы также можете объявить вектор unsigned chars, memcpy вводить в него входное значение, отменить байты на другой вектор и memcpy байты, но это займет порядка больше, чем бит-twiddling, особенно с 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, где все целые числа сохраняются в Big Endian с объяснением «Можно использовать htonl () …» {для замедления типичных программ Windows, если вы не подготовлены}.

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

 #include  #include  template struct SizeT {}; #define BYTESWAPS(bits) \ template inline T htobe(T t, SizeT) { return htobe ## bits(t); } \ template inline T htole(T t, SizeT) { return htole ## bits(t); } \ template inline T betoh(T t, SizeT) { return be ## bits ## toh(t); } \ template inline T letoh(T t, SizeT) { return le ## bits ## toh(t); } BYTESWAPS(16) BYTESWAPS(32) BYTESWAPS(64) #undef BYTESWAPS template inline T htobe(T t) { return htobe(t, SizeT()); } template inline T htole(T t) { return htole(t, SizeT()); } template inline T betoh(T t) { return betoh(t, SizeT()); } template inline T letoh(T t) { return letoh(t, SizeT()); } int main() { std::cout << std::hex; std::cout << htobe(static_cast(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  void SwapEnd(T& var) { char* varArray = reinterpret_cast(&var); for(long i = 0; i < static_cast(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  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(x); // template 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::value); // Si from et to sont du même type on ne swap pas. if (from == to) return value; return SwapBytes(value); } 

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

Если big-endian 16-разрядный unsigned short выглядит как 0xAABB, равный 43707, то тот же 16-разрядный беззнаковый короткий выглядит как 0xBBAA на процессоре little-endian, который также равен 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 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 ++-шаблонная функция и переносная, которая использует только бит-операции.

 template 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 } } 

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

 unsigned long _byteswap_ulong(unsigned long value); 

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

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

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

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

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

Портативная техника для внедрения оптимизаторов-дружественных нестандартных не-inplace 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]; } 

These typedefs have the benefit of raising compiler errors if not used with accessors, thus mitigating forgotten accessor bugs.

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

I recently wrote a macro to do this in C, but it’s equally valid in C++:

 #define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES>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) 

It accepts any type and reverses the bytes in the passed argument. Example usages:

 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 

The above is perfectly copy/paste-able, but there’s a lot going on here, so I’ll break down how it works piece by piece:

The first notable thing is that the entire macro is encased in a do while(0) block. This is a common idiom to allow normal semicolon use after the macro.

Next up is the use of a variable named REVERSE_BYTES as the for loop’s counter. The name of the macro itself is used as a variable name to ensure that it doesn’t clash with any other symbols that may be in scope wherever the macro is used. Since the name is being used within the macro’s expansion, it won’t be expanded again when used as a variable name here.

Within the for loop, there are two bytes being referenced and XOR swapped (so a temporary variable name is not required):

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

__VA_ARGS__ represents whatever was given to the macro, and is used to increase the flexibility of what may be passed in (albeit not by much). The address of this argument is then taken and cast to an unsigned char pointer to permit the swapping of its bytes via array [] subscripting.

The final peculiar point is the lack of {} braces. They aren’t necessary because all of the steps in each swap are joined with the comma operator , making them one statement.

Finally, it’s worth noting that this is not the ideal approach if speed is a top priority. If this is an important factor, some of the type-specific macros or platform-specific directives referenced in other answers are likely a better option. This approach, however, is portable to all types, all major platforms, and both the C and C++ languages.

Here’s how to read a double stored in IEEE 754 64 bit format, even if your host computer uses a different system.

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

For the rest of the suite of functions, including the write and the integer routines see my github project

https://github.com/MalcolmMcLean/ieee754

Try Boost::endian , and DO NOT IMPLEMENT IT YOURSELF!

Here’s a link

Look up bit shifting, as this is basically all you need to do to swap from little -> big endian. Then depending on the bit size, you change how you do the bit shifting.

  • Почему x86 маленький endian?
  • Марширование коллекции байтов байтов в структуру для вытягивания значений
  • Обнаружение цели в программном обеспечении на C ++
  • Давайте будем гением компьютера.