234

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

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


注:私が受け入れた答えは、私が対象としているコンパイラに直接適用されます(これが私が選んだ理由です)。ただし、ここには他にも非常に優れた、より移植性の高い回答があります。

4

34 に答える 34

200

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ビット数(chars)は変換する必要はありません。

また、これらは符号なし整数に対してのみ機能する符号なし値に対してのみ定義されます。

floatとdoubleの場合、プレーン整数の場合と同様に、ホストマシンのバイトオーダーである場合とそうでない場合があるため、より困難です。ビッグエンディアンのマシンではリトルエンディアンのフロートを取得でき、その逆も可能です。

他のコンパイラにも同様の組み込み関数があります。

たとえばGCCでは、ここに記載されているように、いくつかのビルトインを直接呼び出すことができます。

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

(何かを含める必要はありません)。Afaik bits.hは、gcc中心ではない方法でも同じ関数を宣言します。

16ビットスワップそれはほんの少し回転します。

独自にローリングする代わりに組み込み関数を呼び出すと、最高のパフォーマンスとコード密度が得られます。

于 2008-09-19T20:31:38.887 に答える
102

簡単に言えば:

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

于 2011-02-10T11:27:13.820 に答える
86

From The Byte Order Fallacy by Rob Pike:

Let's say your data stream has a little-endian-encoded 32-bit integer. Here's how to extract it (assuming unsigned bytes):

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

If it's big-endian, here's how to extract it:

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

TL;DR: don't worry about your platform native order, all that counts is the byte order of the stream your are reading from, and you better hope it's well defined.

Note: it was remarked in the comment that absent explicit type conversion, it was important that data be an array of unsigned char or uint8_t. Using signed char or char (if signed) will result in data[x] being promoted to an integer and data[x] << 24 potentially shifting a 1 into the sign bit which is UB.

于 2012-04-27T06:51:22.537 に答える
59

ネットワーク/ホストの互換性のためにこれを行う場合は、次を使用する必要があります。

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 つが問題なく機能します。

于 2008-09-19T20:38:18.090 に答える
28

この投稿からいくつかの提案を受け取り、それらをまとめて次のようにしました。

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>
#include <cstdint>

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

次に、次のように使用します。

// swaps val from host-byte-order to network-byte-order
auto swapped = byte_swap<host_endian, network_endian>(val);

およびその逆

// swap a value received from the network into host-byte-order
auto val = byte_swap<network_endian, host_endian>(val_from_network);
于 2010-08-19T14:36:13.650 に答える
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);
}
于 2008-09-19T20:31:54.567 に答える
16

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

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

于 2008-09-19T20:34:46.913 に答える
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);
}
于 2008-09-19T20:29:23.433 に答える
8

Cで行うのと同じ方法:

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

unsigned charsのベクトルを宣言し、その入力値をmemcpyし、バイトを別のベクトルに反転し、バイトをmemcpyすることもできますが、特に64ビット値の場合、ビットをいじるよりも桁違いに長くかかります。

于 2008-09-19T20:30:07.517 に答える
8

異なるプラットフォーム間でデータを転送するためにこれを行っている場合は、ntoh 関数と hton 関数を見てください。

于 2008-09-19T20:26:48.197 に答える
8

ほとんどの 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) が、ホスト順からネットワーク順への変換に役立ちます。

于 2008-09-19T20:33:14.327 に答える
5

真剣に...なぜすべてのソリューションが複雑なのか理解できません! 任意のオペレーティング システムの任意の状況下で任意のサイズの任意の型を交換する、最も単純で最も一般的なテンプレート関数はどうでしょうか????

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はエンディアンが異なります。

于 2014-08-07T08:07:29.387 に答える
5

ほとんどのプラットフォームには、効率的なバイトスワップ機能を提供するシステム ヘッダー ファイルがあります。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
于 2011-02-10T12:02:30.463 に答える
4

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);
}
于 2008-09-20T04:25:11.150 に答える
3

どこにも見たことがないので、ここに独自のソリューションを追加したと思いました。これは、小さくて移植可能な 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;
}
于 2016-01-29T19:30:25.850 に答える
3

スタイルだけでこれが好きです:-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
于 2011-06-26T22:13:17.547 に答える
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]);
    }

免責事項:私はまだこれをコンパイルしたりテストしたりしていません。

于 2008-09-19T21:07:01.627 に答える
2

うわー、ここで読んだ答えのいくつかは信じられませんでした。実際には、これを他の何よりも高速に実行するアセンブリの命令があります。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 CPU 以上でのみ利用可能です。386で実行する予定の人はいますか?!? その場合でも、bswap を次のように置き換えることができます...

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

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

于 2014-09-24T01:47:38.117 に答える
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ポインターにキャストされ、配列の[]添字によるバイトの交換が可能になります。

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

最後に、速度が最優先事項である場合、これは理想的なアプローチではないことに注意してください。これが重要な要素である場合、他の回答で参照されているタイプ固有のマクロまたはプラットフォーム固有のディレクティブのいくつかがより適切なオプションである可能性があります。ただし、このアプローチは、すべての型、すべての主要なプラットフォーム、および C 言語と C++ 言語の両方に移植できます。

于 2016-12-17T07:15:36.217 に答える
0
void writeLittleEndianToBigEndian(void* ptrLittleEndian, void* ptrBigEndian , size_t bufLen )
{
    char *pchLittleEndian = (char*)ptrLittleEndian;

    char *pchBigEndian = (char*)ptrBigEndian;

    for ( size_t i = 0 ; i < bufLen ; i++ )    
        pchBigEndian[bufLen-1-i] = pchLittleEndian[i];
}

std::uint32_t row = 0x12345678;

char buf[4]; 

writeLittleEndianToBigEndian( &row, &buf, sizeof(row) );
于 2021-05-09T14:32:36.423 に答える
-4

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

于 2008-09-19T20:30:37.673 に答える