ホストバイトの順序からリトルエンディアンに値を変換するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/1873352

  •  18-09-2019
  •  | 
  •  

質問

ホストバイトの順序から短い値をリトルエンディアンに変換する必要があります。ターゲットがビッグエンディアンの場合、htons()関数を使用できますが、悲しいかな - そうではありません。

私はできると思います:

swap(htons(val))

しかし、これにより、バイトが2回交換される可能性があり、結果が正しいものになりますが、私の場合は大丈夫ではないパフォーマンスペナルティが得られます。

役に立ちましたか?

解決

次のようなもの:

unsigned short swaps( unsigned short val)
{
    return ((val & 0xff) << 8) | ((val & 0xff00) >> 8);
}

/* host to little endian */

#define PLATFORM_IS_BIG_ENDIAN 1
#if PLATFORM_IS_LITTLE_ENDIAN
unsigned short htoles( unsigned short val)
{
    /* no-op on a little endian platform */
    return val;
}
#elif PLATFORM_IS_BIG_ENDIAN
unsigned short htoles( unsigned short val)
{
    /* need to swap bytes on a big endian platform */
    return swaps( val);
}
#else
unsigned short htoles( unsigned short val)
{
    /* the platform hasn't been properly configured for the */
    /* preprocessor to know if it's little or big endian    */

    /* use potentially less-performant, but always works option */

    return swaps( htons(val));
}
#endif

適切に構成されたシステムがある場合(プリプロセッサがターゲットIDがリトルまたはビッグエンディアンかどうかを知っているように)、「最適化された」バージョンを取得します htoles(). 。それ以外の場合は、潜在的に最適化されていないバージョンを取得します。 htons(). 。いずれにせよ、あなたはうまくいくものを手に入れます。

あまりにもトリッキーで、多かれ少なかれ携帯性はありません。

もちろん、これを実装することにより、最適化の可能性をさらに改善できます inline または、あなたが見るようにマクロとして。

さまざまなコンパイラのエンディアンネスを定義する実際の実装のために、「ポータブルオープンソースハーネス(Posh)」のようなものを見たいかもしれません。注意するには、擬似認証ページを使用する必要があります(個人的な詳細を提供するために登録する必要はありませんが)。 http://hookatooka.com/poshlib/

他のヒント

これは、EndiannessとIBMからそれを決定する方法に関する記事です。

CでEndianに依存しないコードを書く:Endiannessを「Byte」にさせないでください

これには、実行時にエンディアンネスを決定する方法の例が含まれています(これは一度だけする必要があります)

const int i = 1;
#define is_bigendian() ( (*(char*)&i) == 0 )

int main(void) {
    int val;
    char *ptr;
    ptr = (char*) &val;
    val = 0x12345678;
    if (is_bigendian()) {
        printf(“%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
    } else {
        printf(“%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]);
    }
    exit(0);
}

ページには、バイト順序を逆転させる方法に関するセクションもあります。

short reverseShort (short s) {
    unsigned char c1, c2;

    if (is_bigendian()) {
        return s;
    } else {
        c1 = s & 255;
        c2 = (s >> 8) & 255;

        return (c1 << 8) + c2;
    }
}

;

short reverseShort (char *c) {
    short s;
    char *p = (char *)&s;

    if (is_bigendian()) {
        p[0] = c[0];
        p[1] = c[1];
    } else {
        p[0] = c[1];
        p[1] = c[0];
    }

    return s;
}

次に、あなたのエンディアン性を知り、条件付きでhtons()を呼び出す必要があります。実際、htonでさえありませんが、条件付きでバイトを交換するだけです。もちろん、コンパイル時間。

このトリックは、スタートアップで使用する必要があります ntohs ダミー値を使用して、結果の値を元の値と比較します。両方の値が同じ場合、マシンはBig Endianを使用します。そうでなければ、リトルエンディアンです。

次に、aを使用します ToLittleEndian 何もしないか、呼び出す方法 ntohs, 、初期テストの結果に応じて。

(コメントで提供されている情報で編集)

私の心のルールのパフォーマンスの推測は、あなたが小さなエンディアンを一度に大量のデータブロックを単に化しているのか、それとも1つの値のみであるかどうかによって、次の値が異なります。

1つの値だけの場合、関数呼び出しのオーバーヘッドは、おそらく不要なバイトスワップのオーバーヘッドを圧倒します。コンパイラが不必要なバイトスワップを最適化しない場合でも、それはそうです。その後、値をソケット接続のポート番号として記述し、ソケットを開いたりバインドしたりすることを試みます。これには、あらゆる種類のビット操作と比較して年齢が必要です。だから、それについて心配しないでください。

大きなブロックの場合、コンパイラがそれを処理しないのではないかと心配するかもしれません。だからこのようなことをしてください:

if (!is_little_endian()) {
    for (int i = 0; i < size; ++i) {
        vals[i] = swap_short(vals[i]);
    }
}

または、アーキテクチャに関するSIMDの指示を調べてください。

書く is_little_endian() 好きなトリックを使用します。ロバート・S・バーンズが提供するものは健全だと思いますが、特定のターゲットでは、それがビッグエンディアンかどうかを知っているので、おそらくプラットフォーム固有のヘッダーファイルを持っている必要があります。 1または0に評価するマクロ。

いつものように、パフォーマンスを本当に気にかけている場合は、生成されたアセンブリを見て、無意味なコードが削除されたかどうかを確認し、実際に最も速くなるものを見るために互いに互いに様々な代替案を計ります。

残念ながら、標準Cを使用してコンパイル時にシステムのバイト順序を決定するクロスプラットフォームの方法はありません。 #define あなたに config.h (または、あなたまたはあなたのビルドシステムがビルド構成に使用するものは何でも)。

正しい定義をチェックするためのユニットテスト LITTLE_ENDIAN また BIG_ENDIAN このように見えるかもしれません:

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

void check_bits_per_byte(void)
{ assert(CHAR_BIT == 8); }

void check_sizeof_uint32(void)
{ assert(sizeof (uint32_t) == 4); }

void check_byte_order(void)
{
    static const union { unsigned char bytes[4]; uint32_t value; } byte_order =
        { { 1, 2, 3, 4 } };

    static const uint32_t little_endian = 0x04030201ul;
    static const uint32_t big_endian = 0x01020304ul;

    #ifdef LITTLE_ENDIAN
    assert(byte_order.value == little_endian);
    #endif

    #ifdef BIG_ENDIAN
    assert(byte_order.value == big_endian);
    #endif

    #if !defined LITTLE_ENDIAN && !defined BIG_ENDIAN
    assert(!"byte order unknown or unsupported");
    #endif
}

int main(void)
{
    check_bits_per_byte();
    check_sizeof_uint32();
    check_byte_order();
}

多くのLinuxシステムには、aがあります <endian.h> また <sys/endian.h> コンバージョン機能を備えています。 エンディアンの男ページ(3)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top