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

196

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

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

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

Uhall
источник
21
Ntoh Hton будет работать нормально, даже если это не имеет ничего общего с сетью.
Бен Коллинз
2
Лучший способ справиться с порядком байтов в общем случае - убедиться, что код работает как на хостах с младшим, так и на старшем байтах. Если это работает, вы, вероятно, сделали это правильно. Предполагая, что вы используете x86 / be, это опасно для практики.
jakobengblom2
10
hton ntoh не будет работать, если машина с прямым порядком байтов, потому что задающий вопрос явно хочет выполнить преобразование.
Fabspro
6
@ jakobengblom2 - единственный человек, который упомянул это. Почти во всех примерах на этой странице используются такие понятия, как байты «swap» вместо того, чтобы делать это независимо от основной последовательности. Если вы имеете дело с внешними форматами файлов (которые имеют четко определенный порядок байтов), то наиболее переносимым способом является обработка внешних данных как потока байтов и преобразование потока байтов в и из собственных целых чисел. Я передергиваюсь каждый раз, когда вижу short swap(short x)код, так как он сломается, если вы перейдете на платформу с другим порядком байтов. Matthieu M имеет единственный правильный ответ ниже.
Марк Лаката
3
Вы думаете о проблеме совершенно неправильно. Задача не в том, «как преобразовать значения с прямым и младшим порядком байтов». Задача состоит в том, «как преобразовать значения с плавающей запятой и целые числа в определенном формате в собственный формат моей платформы». Если вы все сделаете правильно, нативный формат может быть с прямым или обратным порядком, смешанным или тройным порядком для всех ваших забот кода.
Дэвид Шварц

Ответы:

166

Если вы используете 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 , например , вы можете напрямую позвонить некоторые встроенные команды как описано здесь :

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

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

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

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

Нильс Пипенбринк
источник
11
С GCC я мог бы использовать: #include <byteswap.h> int32_t bswap_32 (int32_t x) int64_t bswap_64 (int64_t x)
jmanning2k
5
__builtin_bswapXдоступно только с GCC-4.3 и далее
Мэтт Джойнер
20
Стоит также отметить , что эти встроенные функции / всегда / своп байт, они не похожи htonl, htonsи т.д. Вы должны знать из контекста ситуации , когда на самом деле поменять байты.
Брайан Ванденберг
8
@ Джейсон, потому что 8-битные числа одинаковы в старшем и младшем порядке. :-)
Нильс Пипенбринк
2
@BrianVandenberg Right; использование htonlи ntohlне беспокоясь о контексте работало бы при написании переносимого кода, так как платформа, определяющая эти функции, могла бы поменять его местами, если он имеет младший / средний порядок байтов, а на старшем - это не будет. Однако при декодировании стандартного типа файла, который определен как little-endian (скажем, BMP), нужно знать контекст и не может просто полагаться на htonlи ntohl.
legends2k
86

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

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

Александр С.
источник
3
Имейте upvote. Я просто использовал учаров и назначил 4 на 1, 3 на 2, от 2 до 3 и от 1 до 4, но это более гибко, если у вас разные размеры. 6 часов на первом поколении Pentium IIRC. BSWAP 1 такт, но зависит от платформы.
2
@RocketRoy: Да, и если скорость оказывается проблемой, очень просто записать перегрузки с интригами, специфичными для платформы и типа.
Александр К.
3
@MihaiTodor: это использование союзов для приведения типов через массив символов явно разрешено стандартом. Смотрите, например. этот вопрос .
Александр С.
4
@AlexandreC. Не в стандарте C ++ - только в C. В C ++ (которым является этот код) этот код является неопределенным поведением.
Раппц
4
@Rapptz: 3.10 кажется ясным: «Если программа пытается получить доступ к сохраненному значению объекта через glvalue другого, чем один из следующих типов, поведение не определено: [...] тип char или unsigned char. ». Может быть, я что-то здесь упускаю, но мне было совершенно ясно, что доступ к любому типу через указатели на символы явно разрешен.
Александр С.
75

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

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

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

Матье М.
источник
6
Это круто, но мне кажется, что это относится только к целым числам и вариантам. Что делать с поплавками / двойниками?
Бретт
1
@ v.oddou: да и нет, файлы с отображением в памяти точно такие же, как сетевые фреймы; если вы соглашаетесь не читать их напрямую, все, что имеет значение, это их порядковый номер: если используется метод с прямым порядком байтов, используйте первую формулу, если она имеет порядок байтов, используйте вторую. Любой компилятор, достойный своей соли, оптимизирует ненужные преобразования, если совпадает порядок байтов.
Матье М.
2
@meowsqueak: Да, я ожидаю, что это сработает, потому что меняется только порядок байтов, а не порядок битов в каждом байте.
Матье М.
3
На слабо связанном примечании, связанный пост - это неприятное чтение ... Парень, кажется, ценит краткость, но предпочитает писать длинную рутину обо всех тех плохих программистах, которые не так просвещены, как в отношении порядка байтов, а не на самом деле объясняя ситуацию и ПОЧЕМУ его решение всегда работает.
Объявление N
1
Если вы используете этот метод, обязательно
приведите
51

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

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

морозный
источник
2
Я считаю, что порядок байтов в сети - это порядок байтов. Эти функции могут использоваться с учетом этого, даже если вы не используете сетевой код. Однако нет версий с плавающей точкой ntohf или htonf
Мэтт
2
Мэтт Х. это только в основном правильно. Не все компьютерные системы имеют порядок байтов с прямым порядком байтов. Если вы работали, скажем, на Motorolla 68K, PowerPC или другой архитектуре с прямым порядком байтов, эти функции вообще не будут менять местами байты, потому что они уже находятся в «Сетевом порядке байтов».
морозное
2
К сожалению, htonlи ntohlне может перейти на little-endian на платформе с прямым порядком байтов.
Брайан Ванденберг
2
@celtschk, понял; тем не менее, OP хочет способ переключения порядка байтов даже в среде с прямым порядком байтов.
Брайан Ванденберг
4
Чтобы избежать неизбежного вопроса: существует ряд причин, по которым требуется LE для платформы BE; ряд форматов файлов (например, bmp, fli, pcx, qtm, rtf, tga) используют значения с прямым порядком байтов ... или, по крайней мере, какая-то версия формата использовалась за один раз в любом случае.
Брайан Ванденберг
26

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

#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);
}
Стив Лоример
источник
Вы также должны включать <cstdint> или <stdint.h>, например, для uint32_t
ADY
17

Процедура перехода от порядкового номера к порядку байтов аналогична переходу от порядкового номера к порядку байтов.

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

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);
}
Kevin
источник
2
Последняя опубликованная здесь функция неверна и должна быть отредактирована в: void swapByteOrder (unsigned long long & ull) {ull = (ull >> 56) | ... (Ull << 56); }
Эрик Бернетт
14
Я не думаю, что правильно использовать логические и (&&), а не побитовые и (&). Согласно спецификации C ++, оба операнда неявно преобразуются в bool, а это не то, что вам нужно.
Тревор Робинсон
15

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

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

anon6439
источник
Это отличная ссылка. Это разожгло мой интерес к х86 ассемблеру.
пп.
1
Сроки результаты для BSWAP представлены здесь. gmplib.org/~tege/x86-timing.pdf ... и здесь ... agner.org/optimize/instruction_tables.pdf
12

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

// 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);
}
отметка
источник
8

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

Андрей
источник
7

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

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

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

Бен Штрауб
источник
7

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

unsigned int change_endian(unsigned 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) будут полезны для преобразования порядка хостов в порядок сетей.

конечная станция
источник
7
Это забавно - стандарт POSIX на opengroup.org/onlinepubs/9699919799/toc.htm не упоминает заголовок «<endian.h>».
Джонатан Леффлер
1
Вы можете использовать htonlи друзей независимо от того, имеет ли сценарий использования какое-либо отношение к сети. Порядок байтов в сети имеет порядок байтов, поэтому просто относитесь к этим функциям как host_to_be и be_to_host. (Не помогает, если вам нужен host_to_le, хотя.)
Питер Кордес
5

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

#define htonl(x) _byteswap_ulong(x)

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

user2699548
источник
4

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

#include <iostream>

#include <endian.h>

template<size_t N> struct SizeT {};

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

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

#undef BYTESWAPS

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

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

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

Вывод:

cafe
deadbeaf
feeddeafbeefcafe
Максим Егорушкин
источник
Изменение: #define BYTESWAPS (биты) \ template <class T> встроенный T htobe (T t, SizeT <bits / 8>) {return htobe ## bits (t); } \ template <class T> встроенный T htole (T t, SizeT <bits / 8>) {return htole ## bits (t); } \ template <class T> встроенный T betoh (T t, SizeT <bits / 8>) {return be ## bits ## toh (t); } \ template <class T> встроенный T letoh (T t, SizeT <bits / 8>) {return le ## bits ## toh (t); }
ldav1s
Спасибо, забыл протестировать betoh () и letoh ().
Максим Егорушкин
4

Мне нравится этот, только для стиля :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
Фридеманн
источник
Я получаю сообщение об ошибке char[]«Ошибка: недопустимый тип не разрешен»
Portland Runner
4

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

template <typename T>
void SwapEnd(T& var)
{
    static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
    std::array<char, sizeof(T)> varArray;
    std::memcpy(varArray.data(), &var, sizeof(T));
    for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    std::memcpy(&var, varArray.data(), sizeof(T));
}

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

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

Пункт 2. Помните о проблемах выравнивания. Обратите внимание, что мы копируем в массив и из него, что является правильным решением, поскольку компилятор C ++ не гарантирует, что мы можем получить доступ к невыровненной памяти (этот ответ был обновлен по сравнению с исходным Форма для этого). Например, если вы выделяете uint64_t, ваш компилятор не может гарантировать, что вы можете получить доступ к 3-му байту этого как uint8_t. Следовательно, правильнее всего скопировать это в массив символов, заменить его, а затем скопировать обратно (так что нет reinterpret_cast). Обратите внимание, что компиляторы в основном достаточно умны, чтобы преобразовать то, что вы делали обратно, в, reinterpret_castесли они способны получить доступ к отдельным байтам независимо от выравнивания.

Чтобы использовать эту функцию :

double x = 5;
SwapEnd(x);

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

Квантовый физик
источник
2
Это будет работать где угодно, но производимая ocde сборки часто будет неоптимальной: см. Мой вопрос stackoverflow.com/questions/36657895/…
j_kubik
Вы используете new/, deleteчтобы выделить буфер для этого?!? sizeof(var)является константой времени компиляции, так что вы можете сделать char varSwapped[sizeof(var)]. Или вы могли бы сделать char *p = reinterpret_cast<char*>(&var)и поменять местами.
Питер Кордес
@ Петр этот ответ быстрый и грязный, чтобы доказать свою точку зрения. Я буду реализовывать ваши предложения. Тем не менее, вам не нужно быть мега SO AH и голосовать за 5-линейное решение, по сравнению с 50-строчными решениями, которые предлагаются там. Я не буду больше говорить.
Квантовый физик
В этом ответе содержится несколько полезных советов о том, как быть осторожным с конструкторами и перегруженными операторами для данных с неправильным порядком байтов, поэтому я был бы рад удалить свое отрицательное голосование, если код не является ужасным, и это то, что хороший компилятор может скомпилировать в bswap. инструкция. Кроме того, я бы предложил использовать for(size_t i = 0 ; i < sizeof(var) ; i++)вместо static_cast<long>. (Или на самом деле своп на месте будет использовать восходящий и нисходящий, char*так что все равно уйдет).
Питер Кордес
например, см . ответ Марка Рэнсома, используя std :: swap для обратного изменения на месте.
Питер Кордес
3

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

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

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

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

  in.value = value;

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

  return out.value;
}

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

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

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

  return SwapBytes<T, sizeof(T)>(value);
}
Матье Паге
источник
3

Если 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))
Адам Фриман
источник
2

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

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

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

Марк Рэнсом
источник
2

Если вы возьмете общий шаблон для изменения порядка битов в слове и выберете часть, которая инвертирует биты в каждом байте, то у вас останется нечто, что инвертирует только байты в слове. Для 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);
sh1
источник
2

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

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}
Joao
источник
2

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

Ник
источник
2

Используя коды ниже, вы можете легко переключаться между 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))
pz64_
источник
1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Райан Хилберт
источник
нашел это где-то в некотором коде. смутил меня. Спасибо за объяснение. Однако почему использование __VA_ARGS__?
asr9
0

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

Сварщик
источник
1
_byteswap_ulongи _uint64(например, в принятом ответе) оба компилируют, чтобы использовать bswapинструкцию. Я был бы удивлен, но интересно узнать, настолько ли быстрее этот асм, поскольку он пропускает только пролог / эпилог - вы его тестировали?
ЗакБ
@stdcall Вопрос не касался переносимого решения и даже не упоминал о платформе. Как сказал мой ответ, вышеизложенное о самом быстром способе прямого обмена. Конечно, если вы пишете это на платформе, отличной от X86, это не сработает, но, как я уже говорил, вы ограничены встроенными компиляторами, если ваш компилятор даже поддерживает их.
Сварщик
@ZachB В данном конкретном случае, я думаю, что пропуск пролога и эпилога даст вам приличную экономию, потому что вы, по сути, выполняете только 1 инструкцию. Прологу придется нажать на стек, выполнить вычитание, установить базовый указатель и затем аналогично в конце. Я не тестировал его, но в вышеприведенном примере есть цепочка зависимостей 0, которую вы просто не сможете получить без обнаженности. Может быть, хороший компилятор включит его, но тогда вы окажетесь в другом балл-парке.
Сварщик
2
Может быть. Но обратите внимание, что в обычном случае замены массива чисел встроенные функции компилятора, обсуждаемые в других ответах, будут использовать расширения SSE / AVX и испускать PSHUFB, который превосходит BSWAP. См. Wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB,
ИМХО плохо публиковать решение для конкретной платформы, когда ОП не указал, что им нужно только решение для x86. И для того, чтобы опровергнуть другие решения, когда ваше решение непригодно для использования на многих очень широко используемых ОС, таких как iOS и Android (которые используют процессоры ARM или MIPS.)
Йенс Альфке
0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

typedef char double_12345678[8]; /* little endian */
typedef char double_78563412[8]; /* Alpha Micro? */
typedef char double_87654321[8]; /* big endian */
BSalita
источник
2
Для этого вопроса тег C ++ имеет значение. Существует много неопределенного поведения из-за C ++ и объединения.
jww
0

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

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

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

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

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

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

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

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

https://github.com/MalcolmMcLean/ieee754

Малкольм Маклин
источник
0

Замена байтов трюком ye olde 3-step-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);
    }
}
Куинн Карвер
источник
0

Похоже, безопасным способом было бы использовать 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); });

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

Если у вас есть C ++ 17, добавьте этот заголовок

#include <algorithm>

Используйте эту функцию шаблона, чтобы поменять местами байты:

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

назвать это как:

swapEndian (stlContainer);
Thinkal VB
источник
-4

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

Redbaron
источник