Cでビッグエンディアンをリトルエンディアンに変換[提供された関数を使用せずに] [終了]


91

Cでビッグエンディアンをリトルエンディアンに変換する関数を記述する必要があります。ライブラリ関数を使用できません。


5
16ビット値?32ビット値?浮く?配列?
John Knoeller、2010

19
おそらく答えを選ぶ時ですか?
Aniket Inge

7
投票を再開します。C ++の場合は、stackoverflow.com / questions / 105252 /…と同じです。編集してそれをより明確にすることができます。
Ciro Santilli郝海东冠状病六四事件法轮功

回答:


167

必要なものが単純なバイトスワップであると仮定して、次のようなことを試してください

符号なし16ビット変換:

swapped = (num>>8) | (num<<8);

符号なし32ビット変換:

swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
                    ((num<<8)&0xff0000) | // move byte 1 to byte 2
                    ((num>>8)&0xff00) | // move byte 2 to byte 1
                    ((num<<24)&0xff000000); // byte 0 to byte 3

これにより、バイト順序が1234から4321にスワップされます。入力がの0xdeadbeef場合、32ビットのエンディアンスワップの出力はになる可能性があります0xefbeadde

上記のコードは、マジックナンバーの代わりにマクロまたは少なくとも定数を使用してクリーンアップする必要がありますが、うまくいけばそのまま

編集:別の答えが指摘したように、プラットフォーム、OS、および命令セット固有の代替手段があり、上記よりもはるかに高速です。Linuxカーネルには、エンディアンを適切に処理するマクロ(cpu_to_be32など)があります。しかし、これらの選択肢は環境に固有です。実際には、エンディアンは、利用可能なアプローチを組み合わせて使用​​するのが最も効果的です。


5
プラットフォーム/ハードウェア固有のメソッドについて言及する場合は+1。プログラムは常に一部のハードウェアで実行され、ハードウェア機能は常に最速です。
eonil 2012

21
16ビット変換がとして行われる((num & 0xff) >> 8) | (num << 8)場合、gcc 4.8.3は単一のrol命令を生成します。また、32ビット変換がとして記述されている((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)場合、同じコンパイラが単一のbswap命令を生成します。
user666412 2015

私はこれがどれほど効率的かはわかりませんが、バイトオーダーを次のようにビットフィールドと入れ替えました struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}。しかし、それが他の提案と同じくらい速いかどうかはわかりません。intの場合は、union { int i; byte_t[sizeof(int)]; }を使用して整数をバイト単位で逆にします。
Ilian Zapryanov 2016

式は次のようにする必要があると思います:(num >> 8)| (num << 8)でバイト順を逆にし、NOT:((num&0xff)>> 8)| (num << 8)、間違った例は下位バイトでゼロを取得します。
jscom 2017

@IlianZapryanov明確にするために+1するかもしれませんが、Cでビットフィールドを使用することは、おそらくそれを行うための最も効率の悪い方法です。
sherrellbc

104

含めることにより:

#include <byteswap.h>

マシンに依存するバイトスワップ関数の最適化されたバージョンを取得できます。その後、次の関数を簡単に使用できます。

__bswap_32 (uint32_t input)

または

__bswap_16 (uint16_t input)

3
あなたの答えのおかげで、私は任意のライブラリ関数を使用することはできません
マーク身代金

4
を読む必要が#include <byteswap.h>あります。.hファイル自体のコメントを参照してください。この投稿には役立つ情報が含まれているため、著者がlib関数を使用しないというOP要件を無視しているにもかかわらず、私は賛成票を投じました。
Eli Rosencruft

30
実際、__ bswap_32 / __ bswap_16関数は実際にはマクロであり、ライブラリ関数ではありません。これは、賛成票を投じるもう1つの理由です。
Eli Rosencruft 2012年

7
私の理解では、このヘッダーはすべてのアーキテクチャのすべてのオペレーティングシステムに存在することが保証されているわけではありません。エンディアンの問題に対処するための移植可能な方法をまだ見つけていません。
エドワードフォーク

2
Windowsには存在しません-少なくともLinuxからmingw 32ビットまたは64ビットでクロスコンパイルするときは
bph

62
#include <stdint.h>


//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val ) 
{
    return (val << 8) | (val >> 8 );
}

//! Byte swap short
int16_t swap_int16( int16_t val ) 
{
    return (val << 8) | ((val >> 8) & 0xFF);
}

//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
    val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | (val >> 16);
}

//! Byte swap int
int32_t swap_int32( int32_t val )
{
    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | ((val >> 16) & 0xFFFF);
}

更新:64ビットバイトスワッピングを追加

int64_t swap_int64( int64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}

uint64_t swap_uint64( uint64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | (val >> 32);
}

以下の場合int32_tint64_t変種のマスキングの背後にある理由何である... & 0xFFFFとは... & 0xFFFFFFFFULL?私が見ないここで符号拡張で何かが起こっていますか?また、なぜswap_int64戻ってくるのuint64_tですか?そうじゃないのint64_t
bgoodr

1
uint64を返すswap_int64は確かにエラーです。符号付き整数値によるマスキングは、実際に符号を削除するためのものです。右にシフトすると、左側に符号ビットが挿入されます。unsigned int swappingオペレーションを呼び出すだけでこれを回避できます。
chmike

ありがとう。swap_int64返答の戻り値の型を変更したい場合があります。参考までに+ 1、BTW!
bgoodr

ビット単位と値のエンディアンは依存していますか?
MarcusJ

1
LLは不要であるの(u)swap_uint64()と同様に、Lは不要です(u)swap_uint32()Uで必要とされないuswap_uint64()ようなあまりUに必要とされていませんuswap_uint32()
復活モニカ- chux

13

これはかなり一般的なバージョンです。私はそれをコンパイルしていないので、おそらくタイプミスがあるでしょうが、あなたは考えを理解するべきです、

void SwapBytes(void *pv, size_t n)
{
    assert(n > 0);

    char *p = pv;
    size_t lo, hi;
    for(lo=0, hi=n-1; hi>lo; lo++, hi--)
    {
        char tmp=p[lo];
        p[lo] = p[hi];
        p[hi] = tmp;
    }
}
#define SWAP(x) SwapBytes(&x, sizeof(x));

注意:これは速度やスペースに最適化されていません。明確で(デバッグが容易)、移植可能であることを目的としています。

2018-04-04を更新 コメント(@chux)で発見された、n == 0の無効なケースをトラップするassert()を追加しました。


1
パフォーマンスを向上させるためにxorSwapを使用できます。サイズ固有のものよりもこの一般的なバージョンを

私はそれをテストしました、これはxorSwap ...よりもx86で速いことがわかりました。stackoverflow.com/questions/3128095/...

1
@nus-非常に単純なコードの利点の1つは、コンパイラオプティマイザが時々非常に高速にできることです。
マイケルJ

@MichaelJ OTOH、chmikeの回答の上記の32ビットバージョンはbswap、最適化を有効にした適切なX86コンパイラーによって単一の命令にコンパイルされます。サイズのパラメーターを持つこのバージョンはそれを行うことができませんでした。
アルニタク2017

@Alnitak-私が言ったように、私はコードを最適化するために努力をしませんでした。ユーザーnusがコードの実行速度が非常に速い(ある場合)ことを見つけたとき、単純なコードはコンパイラによって高度に最適化できることが多いという一般的な考えを述べました。私のコードはさまざまなケースで機能し、理解するのが非常に簡単なので、デバッグも簡単です。それは私の目的を達成しました。
マイケルJ

8

マクロ(組み込みシステムなど)が必要な場合:

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))

これらのマクロは問題ありませんが、((x)>> 24)は、符号付き整数が0x80000000と0xffffffffの間の場合に失敗します。ここではビットごとのANDを使用することをお勧めします。注:((x)<< 24)は完全に安全です。(x)>> 8)は、上位16ビットがゼロ以外の場合も失敗します(または、符号付き16ビット値が提供されます)。

2
@ PacMan--これらのマクロは、符号なし整数のみを交換するために使用することを目的としています。それUINTが彼らの名前にあるのです。
kol 2014年

はい、本当、ノイズのため申し訳ありません。型キャストを埋め込むのが最善ではないでしょうか?

5

編集:これらはライブラリ関数です。それらに従うことはそれを行うための手動の方法です。

__byteswap_ushort、__ byteswap_ulong、および__byteswap_uint64を知らない人の数に私はまったく驚かされます。もちろん、それらはVisual C ++固有のものですが、x86 / IA-64アーキテクチャーでは美味しいコードにコンパイルされます。:)

このページから取得したbswap命令の明示的な使用方法を次に示します上記の組み込みフォームは常に thisよりも高速になることに注意してください。ライブラリルーチンなしで答えを出すためだけに追加しました。

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}

21
Cの質問については、Visual C ++に固有の何かを提案していますか?
Alok Singhal、

3
@Alok:Visual C ++はMicrosoftの製品です。Cコードのコンパイルには問題なく動作します。:)
サムハーウェル、

20
多くの人がMicrosoft固有のバイトスワッピングの実装に気付いていないのに、なぜそれがあなたを驚かすのですか
dreamlax 2010

36
これは、移植性や標準に準拠する必要がないクローズドソース製品を開発している人にとっては良い情報です。
Sam Post

6
@ Alok、OPはコンパイラ| OSについて言及していません。特定のツールセットを使用した経験に応じて、人は答えを出すことができます。
Aniket Inge

5

冗談として:


#include <stdio.h>

int main (int argc, char *argv[])
{
    size_t sizeofInt = sizeof (int);
    int i;

    union
    {
        int x;
        char c[sizeof (int)];
    } original, swapped;

    original.x = 0x12345678;

    for (i = 0; i < sizeofInt; i++)
        swapped.c[sizeofInt - i - 1] = original.c[i];

    fprintf (stderr, "%x\n", swapped.x);

    return 0;
}

7
ははははは。ははは。ハ。は?(冗談は何ですか?)

3
これをいくつかのWindowsソースリポジトリからプルしましたか?:)
hochl 2016年

Nodejsはこのテクニックを使用します!github.com/nodejs/node/blob/…–
Justin Moser

int i, size_t sizeofInt両方に同じタイプではなく、使用することに興味があります。
chux-モニカを復活させる

5

4 intの倍数であると仮定して、Intel組み込み関数を使用してSSSE3命令pshufbを使用する方法を次に示します。

unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) {
    int i;
    __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
    for (i = 0; i < length; i += 4) {
        _mm_storeu_si128((__m128i *)&destination[i],
        _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask));
    }
    return destination;
}

3

これは動作しますか/速くなりますか?

 uint32_t swapped, result;

((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];

2
私はあなたが意味すると思うchar、ではありませんbyte
dreamlax 2010

この戦略を使用すると、投票数があなたの投票数よりも多いソリューションは同等であり、最も効率的で移植性があります。しかし、私が提案するソリューション(2番目に投票数が多い)は、必要な操作が少なく、より効率的です。
chmike

1

これが私が使用している関数です-テストされ、あらゆる基本的なデータ型で動作します:

//  SwapBytes.h
//
//  Function to perform in-place endian conversion of basic types
//
//  Usage:
//
//    double d;
//    SwapBytes(&d, sizeof(d));
//

inline void SwapBytes(void *source, int size)
{
    typedef unsigned char TwoBytes[2];
    typedef unsigned char FourBytes[4];
    typedef unsigned char EightBytes[8];

    unsigned char temp;

    if(size == 2)
    {
        TwoBytes *src = (TwoBytes *)source;
        temp = (*src)[0];
        (*src)[0] = (*src)[1];
        (*src)[1] = temp;

        return;
    }

    if(size == 4)
    {
        FourBytes *src = (FourBytes *)source;
        temp = (*src)[0];
        (*src)[0] = (*src)[3];
        (*src)[3] = temp;

        temp = (*src)[1];
        (*src)[1] = (*src)[2];
        (*src)[2] = temp;

        return;
    }

    if(size == 8)
    {
        EightBytes *src = (EightBytes *)source;
        temp = (*src)[0];
        (*src)[0] = (*src)[7];
        (*src)[7] = temp;

        temp = (*src)[1];
        (*src)[1] = (*src)[6];
        (*src)[6] = temp;

        temp = (*src)[2];
        (*src)[2] = (*src)[5];
        (*src)[5] = temp;

        temp = (*src)[3];
        (*src)[3] = (*src)[4];
        (*src)[4] = temp;

        return;
    }

}

2
コードは非常に合理的な仮定に依存してsourceいます。必要に応じて調整されますが、その仮定が満たされない場合、コードはUBです。
chux-モニカを復活させる

1

編集:この関数は、整列された16ビットワードのエンディアンのみを交換します。UTF-16 / UCS-2エンコーディングによく必要な関数。編集終了。

メモリブロックのエンディアンを変更する場合は、私の非常に高速なアプローチを使用できます。メモリアレイのサイズは8の倍数にする必要があります。

#include <stddef.h>
#include <limits.h>
#include <stdint.h>

void ChangeMemEndianness(uint64_t *mem, size_t size) 
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;

size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
  *mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}

この種の関数は、Unicode UCS-2 / UTF-16ファイルのエンディアンを変更するのに役立ちます。


CHAR_BIT #defineは、コードを完成させるために欠落しています。
–TõnuSamuel 2013

わかりました、不足しているインクルードを追加しました。
PatrickSchlüter2013年

ここにC ++でのスワップへのリンクがあります、私t know if itは提案ほど速くありませんが、それはうまくいきません:github.com/heatblazer/helpers/blob/master/utils.h
Ilian Zapryanov

CHAR_BIT代わりの8よう好奇心旺盛である0xFF00FF00FF00FF00ULLに依存していますCHAR_BIT == 8LL定数では必要ないことに注意してください。
chux-モニカを復活させる

あなたは正しいchuxです。CHAR_BITそのマクロの露出を増やすためにのみ書いた。LLに関しては、何よりもアノテーションです。また、バグのあるコンパイラー(標準に準拠していないもの)を使用して、昔から気付いていた習慣も正しくありませんでした。
PatrickSchlüter18年

1

このコードスニペットは、32ビットのリトルエンディアン数をビッグエンディアン数に変換できます。

#include <stdio.h>
main(){    
    unsigned int i = 0xfafbfcfd;
    unsigned int j;    
    j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24);    
    printf("unsigned int j = %x\n ", j);    
}

ありがとう@YuHao私はここで新しいのですが、テキストのフォーマット方法がわかりません。
Kaushal Billore 2013

2
((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);一部のプラットフォームでは、使用が高速になる場合があります(例:ANDマスク定数のリサイクル)。ほとんどのコンパイラはこれを行いますが、一部の単純なコンパイラでは最適化できません。

-7

x86またはx86_64プロセッサで実行している場合、ビッグエンディアンはネイティブです。そう

16ビット値の場合

unsigned short wBigE = value;
unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);

32ビット値の場合

unsigned int   iBigE = value;
unsigned int   iLittleE = ((iBigE & 0xFF) << 24)
                        | ((iBigE & 0xFF00) << 8)
                        | ((iBigE >> 8) & 0xFF00)
                        | (iBigE >> 24);

コンパイラがこれがバイトレベルの操作であることを認識し、バイトスワップコードを生成しない限り、これは最も効率的なソリューションではありません。しかし、それはメモリレイアウトのトリックに依存せず、かなり簡単にマクロに変えることができます。


25
x86およびx86_64アーキテクチャでは、リトルエンディアンスキームがネイティブスキームです。
MK別名グリス2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.