C ++でビッグエンディアンとリトルエンディアンの値を変換するにはどうすればよいですか?


196

C ++でビッグエンディアンとリトルエンディアンの値を変換するにはどうすればよいですか?

編集:わかりやすくするために、1つのCPUアーキテクチャから別のCPUアーキテクチャにバイナリデータ(倍精度浮動小数点値と32ビットおよび64ビット整数)を変換する必要があります。これはネットワーキングを含まないため、ntoh()および類似の関数はここでは機能しません。

編集#2:私が受け入れた答えは、対象としているコンパイラに直接適用されます(そのため、私はそれを選択しました)。ただし、他にも非常に優れた、より移植性の高い答えがあります。


21
ntoh htonは、ネットワークとは何の関係もない場合でも、正常に機能します。
ベンコリンズ

2
一般にエンディアンに対処する最良の方法は、コードがリトルエンディアンとビッグエンディアンの両方のホストマシンで実行されるようにすることです。それが機能する場合、おそらく正しく実行しました。x86 / beを使用していると想定すると、練習として危険です。
jakobengblom2 2008年

10
質問の質問者が明示的に変換を実行する必要があるため、マシンがビッグエンディアンの場合、hton ntohは機能しません。
fabspro 2011年

6
@ jakobengblom2がこれに言及する唯一の人物です。このページのほとんどすべての例では、基礎となるエンディアンにとらわれることなく、「スワップ」バイトなどの概念を使用しています。外部ファイル形式(エンディアンが明確に定義されている)を扱う場合、最もポータブルなことは、外部データをバイトストリームとして扱い、バイトストリームをネイティブ整数との間で変換することです。short swap(short x)エンディアンが異なるプラットフォームに移動するとコードが壊れてしまうので、コードを見るたびにうずくまります。Matthieu Mは、以下の唯一の正しい答えを持っています。
Mark Lakata 2013年

3
あなたは問題について完全に間違っていると考えています。タスクは、「ビッグエンディアンとリトルエンディアンの値をどのように変換するか」ではありません。タスクは、「特定の形式の浮動小数点値と整数値をプラットフォームのネイティブ形式に変換する方法」です。これを正しく行うと、ネイティブフォーマットは、すべてのコードで、ビッグエンディアン、リトルエンディアン、混合エンディアン、または3値になります。
David Schwartz、

回答:


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以降でのみ利用可能です
マットジョイナー

20
これは、これらの組み込み関数は/いつも/スワップバイト、そうではないようにしていることは注目にも価値があるhtonlhtonsなど、あなたがバイトを実際に交換する際にあなたの状況の文脈から知っている必要があります。
ブライアンヴァンデンバーグ

8
@Jason。8ビットの数値はビッグエンディアンとリトルエンディアンで同じです。:-)
Nils Pipenbrinck 2013

2
@BrianVandenbergそうです。使用htonlしてntohl、それは少し/ミッドエンディアンと上だ場合、それを交換するだろう、これらの関数を定義するプラットフォーム以来、ポータブルなコードを書くときに働くだろう文脈を気にすることなく、ビッグエンディアンそれは無操作しないと思います。ただし、リトルエンディアン(BMPなど)として定義されている標準のファイルタイプをデコードする場合でも、コンテキストを知る必要がhtonlあり、とだけに頼ることはできませんntohl
legends2k 2015

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
賛成票を持っています。私はucharを使用し、4対1、3対2、2対3、および1対4を割り当てましたが、サイズが異なる場合、これはより柔軟です。第1世代Pentium IIRCで6クロック。BSWAPは1クロックですが、プラットフォーム固有です。

2
@RocketRoy:はい、そして速度が問題であることが判明した場合、プラットフォーム固有およびタイプ固有の組み込みでオーバーロードを記述するのは非常に簡単です。
Alexandre C.

3
@MihaiTodor:charの配列を介して型キャストするためのこの共用体の使用は、標準で明示的に許可されています。たとえばを参照してください。この質問
Alexandre C.

4
@AlexandreC。C ++標準にはありません-Cのみです。C++(これはこのコードです)では、このコードは未定義の動作です。
Rapptz 14

4
@Rapptz:3.10は明確に思われます:「プログラムが次のタイプのいずれか以外のglvalueを介してオブジェクトの格納された値にアクセスしようとすると、動作は未定義です:[...] charまたはunsigned charタイプ。ここに何か足りないかもしれませんが、charポインタを介して任意の型へのアクセスが明示的に許可されていることは、私にはかなり明白でした。
Alexandre C.

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_tsigned charまたはchar(符号付きの場合)を使用data[x]すると、整数に昇格され、data[x] << 24UBである符号ビットに1がシフトされる可能性があります。


5
これはすばらしいですが、整数とバリアントにのみ適用されるように思えます。float / doubleをどうするか?
Brett

1
@ v.oddou:はい、いいえ、メモリマップファイルはネットワークフレームとまったく同じです。それらを直接読み取らないことを受け入れる場合、重要なのはそれらのエンディアンです。リトルエンディアンの場合は最初の式を使用し、ビッグエンディアンの場合は2番目の式を使用します。エンディアンが一致する場合、その価値のあるコンパイラは不要な変換を最適化します。
Matthieu M.13年

2
@meowsqueak:はい、機能します。バイトの順序のみが変更され、各バイト内のビットの順序は変更されないためです。
Matthieu M.

3
大まかに関連するメモで、リンクされた投稿は不愉快な読み物です...男は簡潔さを重視しているようですが、彼はエンディアンについてではなく、実際にはエンディアンに関しては啓蒙されていないすべての悪いプログラマについて長い怒りを書くことを好みました状況と彼の解決策が常に機能する理由を説明します。
Ad N

1
この方法を使用している場合は、データを(unsigned 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ソリューションの1つが問題なく機能します。


2
ネットワークのバイト順はビッグエンディアンだと思います。これらの関数は、ネットワークコードを使用していない場合でも、そのことを考慮して使用できます。ただし、フロートバージョンntohfまたはhtonfはありません
Matt

2
マットH.それはほとんど正しいです。すべてのコンピューターシステムにリトルエンディアンのバイト順があるわけではありません。モトローラ68k、PowerPC、または別のビッグエンディアンアーキテクチャで作業している場合、これらの関数はすでに「ネットワークバイトオーダー」になっているため、バイトを交換しません。
Frosty

2
残念ながら、htonlntohlビッグエンディアンのプラットフォームでリトルエンディアンに行くことができません。
ブライアンヴァンデンバーグ

2
@celtschk、理解されました。ただし、OPは、ビッグエンディアン環境でも、エンディアンを切り替える方法を求めています。
ブライアンヴァンデンバーグ

4
避けられない質問を回避するには、BEプラットフォームにLEが必要な理由はいくつかあります。いくつかのファイル形式(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);
}

たとえば、uint32_tの場合、<cstdint>または<stdint.h>も含める必要があります
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);
}

2
ここに投稿された最後の関数は正しくないため、次のように編集する必要があります。void swapByteOrder(unsigned long long&ull){ull =(ull >> 56)| ...(ull << 56); }
エリックバーネット

14
私は、ビット単位のAND(&)ではなく、論理AND(&&)を使用するのが正しいとは思いません。C ++仕様によると、両方のオペランドは暗黙的にboolに変換されますが、これは必要なことではありません。
Trevor Robinson、

16

BSWAPと呼ばれるアセンブリ命令があり、非常に高速にスワップが行われます。あなたはそれについてここで読むことができます

Visual Studio、より正確にはVisual C ++ランタイムライブラリには、このためのと呼ばれるプラットフォーム組み込み機能があります_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()。他のプラットフォームにも同様のものが存在するはずですが、それらが何と呼ばれるかはわかりません。


それは素晴らしいリンクです。x86アセンブラへの興味が再燃しました。
PP。

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

また、符号なし文字のベクトルを宣言し、入力値をそれにmemcpyし、バイトを別のベクトルに反転し、バイトをmemcpyすることもできますが、特に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)が役立ちます。


6
おかしいです-opengroup.org/onlinepubs/9699919799/toc.htmのPOSIX標準では、ヘッダー '<endian.h> `について言及されていません。
Jonathan Leffler、2010年

1
htonlユースケースがネットワーキングと関係があるかどうかに関係なく、友達や友人を使用できます。ネットワークバイトオーダーはビッグエンディアンなので、これらの関数をhost_to_beおよびbe_to_hostとして扱います。(ただし、host_to_leが必要な場合は役に立ちません)
Peter Cordes

5

少なくともWindowsの場合、htonl()は、組み込みの対応する_byteswap_ulong()よりもはるかに遅いことに注意してください。前者はws2_32.dllへのDLLライブラリ呼び出しであり、後者は1つのBSWAPアセンブリ命令です。したがって、プラットフォームに依存するコードを記述している場合は、速度のために組み込み関数を使用することをお勧めします。

#define htonl(x) _byteswap_ulong(x)

これは、すべての整数がビッグエンディアンで保存され、「htonl()を使用できる...」という{PNG画像処理で特に重要になる可能性があります。


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(bits)\ template <class T> inline 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()をテストするのを忘れました。
Maxim Egorushkin、2011

4

私はこれだけ好きです、スタイルのためだけです:-)

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

私は、上のエラーを取得するchar[]「エラー:不完全な型が許可されていない」と言って
ポートランドランナーを

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文字ずつ入れ替えるだけです。

ポイント1:演算子なし:単純な代入演算子「=」を使用しなかったことを思い出してください。エンディアンが反転すると一部のオブジェクトがめちゃくちゃになり、コピーコンストラクター(または代入演算子)が機能しないためです。したがって、文字ごとにコピーする方が信頼性が高くなります。

ポイント2:アライメントの問題に注意してください:配列との間でコピーしていることに注意してください。これは、C ++コンパイラーがアライメントされていないメモリにアクセスできることを保証しないためです(この回答は元から更新されました)このフォーム)。たとえば、を割り当てたuint64_t場合、コンパイラはその3番目のバイトにとしてアクセスできることを保証できませんuint8_t。したがって、正しいことは、これをchar配列にコピーし、それを交換してから、コピーして戻すことです(そのため、はありませんreinterpret_cast)。コンパイラーはほとんどのreinterpret_cast場合、整列に関係なく個々のバイトにアクセスできる場合は、実行した内容をaに変換するのに十分スマートです。

この機能を使用するには

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)、その場で交換できます。
Peter Cordes

@ピーターこの答えは、ポイントを証明するために作られた迅速で汚いです。あなたの提案を実装します。ただし、メガSO AHである必要はなく、5行のソリューションは、そこに与えられている50行のソリューションと比較して反対票を投じる必要はありません。これ以上言うつもりはありません。
量子物理学者

この答えは、間違ったエンディアンのデータでコンストラクターとオーバーロードされた演算子に注意することについていくつかの有用なポイントを作るので、コードがひどくなくて、良いコンパイラーがbswapにコンパイルできるものであれば、私は反対票を削除できます命令。また、for(size_t i = 0 ; i < sizeof(var) ; i++)ではなくを使用することをお勧めしstatic_cast<long>ます。(または、実際には、インプレーススワップは昇順と降順を使用するchar*ので、とにかくなくなります)。
Peter Cordes

たとえば、インプレースを逆にするstd :: swapを使用してMark Ransomの回答を参照してください。
Peter Cordes

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ビット符号なし整数が2864434397に等しい0xAABBCCDDのように見える場合、その同じ32ビット符号なし整数は、同じく2864434397に等しいリトルエンディアンプロセッサでは0xDDCCBBAAのように見えます。

ビッグエンディアンの16ビット符号なしshortが43707に等しい0xAABBのように見える場合、その同じ16ビットの符号なしshortも、同じく43707に等しいリトルエンディアンプロセッサ上の0xBBAAのように見えます。

リトルエンディアンからビッグエンディアンにバイトを交換するための便利な#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);

これは通常、ほとんどのアーキテクチャで単一の回転命令に単純化されます(操作全体がおそらく1つの命令であることを無視して)。

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ループ内では、2バイトが参照され、XORがスワップされます(したがって、一時的な変数名は必要ありません)。

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

__VA_ARGS__マクロに与えられたものを表し、渡されるものの柔軟性を高めるために使用されます(ただし、それほど多くはありません)。次に、この引数のアドレスが取得され、unsigned charポインターにキャストされて、配列の[]添え字によるバイトのスワッピングが可能になります。

最後の特異点は、{}ブレースがないことです。各スワップのすべてのステップがカンマ演算子で結合され、1つのステートメントになるため、これらは必要ありません。

最後に、速度が最優先事項である場合、これは理想的なアプローチではないことに注意してください。これが重要な要素である場合、他の回答で参照されているタイプ固有のマクロまたはプラットフォーム固有のディレクティブのいくつかが、より良いオプションである可能性があります。ただし、このアプローチは、すべてのタイプ、すべての主要なプラットフォーム、および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);

xchg al、ahを使用することを除いて、16ビットの処理は同じくらい簡単です。bswapは32ビットレジスタでのみ機能します。

64ビットは少しトリッキーですが、過度にトリッキーではありません。ループやテンプレートなどを使用した上記のすべての例よりもはるかに優れています。

ここにいくつかの警告があります...まず、bswapは80x486 CPU以上でのみ利用可能です。386で実行する予定はありますか?その場合は、bswapを次のように置き換えることができます...

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

また、インラインアセンブリはVisual Studioのx86コードでのみ使用できます。ネイキッド関数を並べることはできず、x64ビルドでも使用できません。その場合、コンパイラ組み込み関数を使用する必要があります。


1
_byteswap_ulong_uint64(たとえば、承認された回答で)どちらもコンパイルしてbswap命令を使用します。私は驚くでしょうが、このasmがプロローグ/エピローグを省略しているだけなので、これがもっと速いのかどうか興味があります-ベンチマークしましたか?
ZachB、2015年

@stdcallこの質問は、ポータブルなソリューションを要求したり、プラットフォームについては何も言及していませんでした。私の答えが言ったように、上記はエンディアンスワップへの最速の方法についてです。もちろん、X86以外のプラットフォームでこれを作成している場合、これは機能しませんが、前述したように、コンパイラーがサポートしている場合でも、コンパイラー組み込み関数に制限されます。
溶接機

@ZachBこの特定のケースでは、基本的に1つの命令しか実行していないため、プロローグとエピローグを省略すると、かなりの節約ができると思います。プロローグはスタックにプッシュし、減算を行い、ベースポインターを設定し、最後に同様にする必要があります。私はそれをベンチマークしていませんが、上記は依存関係チェーンが0であり、ネイキッドにならないと取得できません。多分良いコンパイラはそれをインライン化するかもしれませんが、それからあなたは別の球場にいます。
溶接機

2
たぶん。ただし、数値の配列を交換する一般的なケースでは、他の回答で説明されているコンパイラ組み込み関数がSSE / AVX拡張を使用し、BSWAPよりも優れたPSHUFBを生成することに注意してください。wm.ite.pl/articles/reverse-array-of-bytes.htmlを
ZachB

OPがx86のソリューションのみを必要とすることを指定しなかった場合、プラットフォーム固有のソリューションを投稿することはIMHOの悪い形です。また、他のソリューションを非難するために、iOSやAndroid(ARMまたはMIPS CPUを使用する)などの非常に広く使用されている多くのOSで使用できない場合
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];
}

これらのtypedefには、アクセサーと共に使用しないとコンパイラー・エラーが発生するという利点があるため、アクセサーの忘れられたバグを軽減できます。

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 ++とunionにより、未定義の動作がたくさんあります。
jww 2017

0

ホストコンピュータが別のシステムを使用している場合でも、IEEE 754 64ビット形式で格納されているdoubleを読み取る方法を次に示します。

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

テンプレート関数のピボット周りの古い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

安全な方法は、各単語にhtonを使用することだと思われます。だから、あなたが持っているなら...

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

これは基本的にリトル->ビッグエンディアンからスワップするために必要なすべてのことなので、ビットシフトを調べます。次に、ビットサイズに応じて、ビットシフトの方法を変更します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.