Як конвертувати між великими ендіанськими та малі-ендіанськими значеннями в C ++?


196

Як конвертувати між великими ендіанськими та малі-ендіанськими значеннями в C ++?

EDIT: Для наочності я повинен перекладати двійкові дані (подвійні точні значення плаваючої точки та 32-бітні та 64-бітні цілі числа) з однієї архітектури процесора в іншу. Це не передбачає мереж, тому ntoh () та подібні функції тут не працюватимуть.

EDIT № 2: Відповідь, яку я прийняв, стосується безпосередньо компіляторів, на які я націлююсь (саме тому я вибрав її). Однак є й інші дуже хороші, більш портативні відповіді.


21
ntoh hton буде працювати нормально, навіть якщо він не має нічого спільного з мережею.
Бен Коллінз

2
Найкращий спосіб боротьби з ендіанічністю взагалі - це переконатися, що код працює як на хост-машинах малої, так і на великих ендіан. Якщо це працює, ви, ймовірно, зробили це правильно. Якщо припустити, що ви на x86 / бути небезпечним, як практика.
jakobengblom2

10
hton ntoh не працюватиме, якщо машина є великим ендіаністом, оскільки запитуючий питання явно хоче виконати перетворення.
fabspro

6
@ jakobengblom2 - це єдина особа, яка згадує про це. Практично у всіх прикладах на цій сторінці використовуються такі поняття, як "поміняти" байтами, замість того, щоб робити це агностиком основної ендіанності. Якщо ви маєте справу з зовнішніми форматами файлів (які мають чітко визначену цінність), найбільш портативним, що потрібно зробити, є трактування зовнішніх даних як байтовий потік та перетворення потоку байтів у цілі цілі числа та з них. Я колюсь кожного разу, коли бачу short swap(short x)код, оскільки він зламається, якщо ви перейдете на платформу з різною витривалістю. Маттьє М має єдину правильну відповідь нижче.
Марк Лаката

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-бітний своп - це просто біт-обертання.

Виклик внутрішніх даних замість того, щоб прокручувати власні, дає вам найкращу продуктивність та щільність коду.


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 і далі
Matt Joiner

20
Варто також відзначити , що ці вбудовані функції / завжди / своп байт, вони не схожі htonl, htonsі т.д. Ви повинні знати з контексту ситуації , коли насправді поміняти байти.
Брайан Ванденберг

8
@Jason, тому що 8-бітові числа однакові у великих та маленьких ендіан. :-)
Нілс Піпенбрінк

2
@BrianVandenberg Right; використання htonlта ntohlбез занепокоєння контексту буде працювати при написанні портативного коду, оскільки платформа, що визначає ці функції, мінятиме його, якщо він маленький / середній-ендіанський, а на великому-ендіанській - це не-оп. Однак при розшифровці типового типу файлу, який визначається як мало-ендіанський (скажімо, 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
Майте нагороду. Я просто використав учар і призначив 4 до 1, 3 до 2, 2 до 3 та 1 до 4, але це більш гнучко, якщо у вас різні розміри. 6 годин на Пентіум 1-го роду IIRC. BSWAP - 1 годинник, але залежить від платформи.

2
@RocketRoy: Так, і якщо швидкість виявляється проблемою, писати перевантаження за допомогою певних платформних та типових інтрисиків дуже просто.
Олександр К.

3
@MihaiTodor: Це використання об'єднань для набору тексту через масив символів явно дозволено стандартом. Див. Напр. це питання .
Олександр К.

4
@AlexandreC. Не в стандарті C ++ - лише в C. У C ++ (який цей код) цей код не визначений.
Rapptz

4
@Rapptz: 3.10 видається зрозумілим: "Якщо програма намагається отримати доступ до збереженого значення об'єкта за допомогою glvalue іншого, ніж одного з наступних типів, поведінка не визначена: [...] char або неподписаний тип char. ". Можливо, мені чогось тут не вистачає, але мені було цілком зрозуміло, що доступ до будь-якого типу через покажчики char явно дозволяється.
Олександр Ч.

75

Із помилок порядку байтів Роб Пайк:

Скажімо, у вашому потоці даних є 32-бітове ціле число, кодоване малим ендіаном. Ось як витягнути його (припускаючи неподписані байти):

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

Якщо це великий ендіан, ось як його витягнути:

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

TL; DR: не турбуйтеся про власний порядок на вашій платформі. Все, що враховується, - це порядок байт потоку, з якого ви читаєте, і ви краще сподіваєтесь, що він добре визначений.

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


6
Це круто, але мені здається, що це стосується лише цілих чисел та варіантів. Що робити з поплавцями / парними?
Бретт

1
@ v.oddou: так і ні, файли, відображені в пам'яті, точно такі ж, як мережеві кадри; якщо ви погоджуєтесь не читати їх безпосередньо, все важливо - це їхня ендіантність: якщо маленький-ендіанець, використовуйте першу формулу, якщо вона є великою-ендіанською, використовуйте другу. Будь-який компілятор, що коштує його солі, оптимізує непотрібні перетворення, якщо витривалість відповідає.
Матьє М.

2
@meowsqueak: Так, я б очікував, що це спрацює, тому що змінюється лише порядок байтів, а не порядок бітів у кожному байті.
Матьє М.

3
На слабко пов’язаній замітці пов’язаний пост є неприємним читанням ... Хлопець, здається, цінує стислість, але він вважає за краще написати довгу сказу про всіх тих поганих програмістів, які не такі просвітлені, як він, щодо ендіанства, а не насправді пояснення ситуації та ЧОМУ його рішення завжди працює.
Оголошення N

1
Якщо ви використовуєте цей метод, переконайтеся, що ви передали свої дані (без підпису char *)
joseph

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
Matt

2
Метт Х., це лише здебільшого правильно. Не всі комп'ютерні системи мають порядок байтів з малою мірою. Якщо ви працювали над, скажімо, мотороллою 68k, PowerPC або іншою архітектурою великого ендіану, ці функції взагалі не замінюватимуть байти, оскільки вони вже перебувають у "мережевому байті".
Морозний

2
На жаль, htonlі ntohlне може перейти на маленького ендіана на платформі big-endian.
Брайан Ванденберг

2
@celtschk, зрозумів; однак ОП хоче отримати спосіб переключення на витривалість навіть у середовищі великих ендіантів.
Брайан Ванденберг

4
Щоб уникнути неминучого питання: є ряд причин, що потребують ЛЕ для платформи 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

Процедура переходу від big-endian до little-endian така сама, як перехід від little-endian до big-endian.

Ось приклад коду:

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

2
Остання функція, розміщена тут, неправильна, і її слід редагувати таким чином: void swapByteOrder (неподписаний довгий довгий & ull) {ull = (ull >> 56) | ... (ull << 56); }
Ерік Бернетт

14
Я не думаю, що правильно використовувати логічне і (&&) на відміну від побітових і (&). Відповідно до специфікації C ++, обидва операнди неявно перетворюються на bool, а це не те, що потрібно.
Тревор Робінсон

15

Існує інструкція по збірці під назвою BSWAP, яка зробить своп замість вас надзвичайно швидко . Про це можна прочитати тут .

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


Це чудове посилання. Це знову викликало мій інтерес до асемблера x86.
ПП.

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, якщо ви не готові}.


4

Більшість платформ мають файл заголовка системи, який забезпечує ефективні функції перемикання байтів. У 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 (біт) \ шаблон <клас T> вбудований T htobe (T t, SizeT <біт / 8>) {повернути htobe ## біт (t); } \ шаблон <клас T> вбудований t htole (T t, розмірT <біт / 8>) {повернути htole ## біт (t); } \ шаблон <клас T> вбудований T betoh (T t, SizeT <біт / 8>) {return be ## bits ## toh (t); } \ шаблон <клас T> вбудований T letoh (T t, SizeT <біт / 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 : Немає операторів: Пам'ятайте, що я не використовував простий оператор присвоєння "=", тому що деякі об'єкти будуть заплутані при переверненні нестабільності і конструктор копій (або оператор призначення) не працюватиме. Тому надійніше копіювати їх за допомогою чару.

Пункт 2 : Будьте в курсі проблем з вирівнюванням: Зауважте, що ми копіюємо в масив і з нього, що правильно робити, тому що компілятор C ++ не гарантує, що ми можемо отримати доступ до невідповідної пам’яті (ця відповідь була оновлена ​​з оригіналу форма для цього). Наприклад, якщо ви виділите uint64_t, ваш компілятор не може гарантувати, що ви можете отримати доступ до 3-го байту як uint8_t. Тому правильне зробити - скопіювати це в масив char, поміняти його по повітрю, а потім скопіювати назад (так ні 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);

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

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

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вказівнику, щоб дозволити заміну його байтів через масив[] підписку .

Останнім своєрідним моментом є відсутність {}брекетів. Вони не потрібні, тому що всі кроки кожного swap з'єднуються з оператором коми , що робить їх одним твердженням.

Нарешті, варто зазначити, що це не ідеальний підхід, якщо швидкість є головним пріоритетом. Якщо це важливий фактор, деякі з макросів, характерних для типу, або директив, орієнтованих на платформу, на які посилаються в інших відповідях, ймовірно, є кращим варіантом. Цей підхід, однак, є портативним для всіх типів, усіх основних платформ та обох мов 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інструкції. Я був би здивований, але мені цікаво дізнатись, чи є ця суть набагато швидшою, оскільки вона лише опускає пролог / епілог - ви її орієнтували?
ZachB

@stdcall Питання не запитувало портативного рішення і навіть нічого не згадувало про платформу. Як я сказав у відповіді, вищесказане стосується найшвидшого способу заміни. Звичайно, якщо ви пишете це на платформі, що не стосується X86, це не спрацює, але, як я вже згадував, ви обмежуєтесь внутрішніми характеристиками компілятора, якщо ваш компілятор навіть підтримує їх.
The Welder

@ZachB У цьому конкретному випадку я думаю, що опускання прологу та епілогу дасть вам гідну економію, оскільки ви по суті виконуєте лише 1 інструкцію. Пролог повинен буде натиснути на стек, зробити віднімання, встановити базовий покажчик і потім подібне в кінці. Я не визначав це, але вищезазначене має ланцюжок залежності 0, якого ви просто не збираєтеся отримати, не будучи голим. Можливо, хороший компілятор би наклав це, але тоді ви перебуваєте в іншому кульовому парку.
The Welder

2
Можливо, Але зауважте, що у звичайному випадку заміни масиву чисел, вбудовані компоненти компілятора, обговорювані в інших відповідях, використовуватимуть розширення SSE / AVX та випромінюють PSHUFB, що перевершує BSWAP. Дивіться wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB

Це погана форма IMHO для розміщення конкретного платформного рішення, коли в ОП не було вказано, що їм потрібно лише рішення для x86. І щоб знехтувати іншими рішеннями, коли ваше непридатне для багатьох дуже широко використовуваних ОС, таких як iOS та Android (які використовують процесори ARM або MIPS.)
Jens Alfke

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

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

Переміщення байтів на тридцятирічний трюк у фокусі функції шаблону навколо шарніра у функції шаблону дає гнучко, швидке рішення 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

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

-4

Подивіться біт зміщення, тому що це в основному все, що вам потрібно зробити, щоб поміняти місцями на маленький -> великий ендіан. Потім, залежно від розміру біта, ви змінюєте спосіб зміни біта.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.