web-dev-qa-db-ja.com

エンディアンが要因になるのはいつですか?

私が理解していることからのエンディアンは、少なくとも最も一般的なケースでは、マルチバイトワードを構成するバイトの順序が異なる場合です。 16ビット整数を0xHHLLまたは0xLLHHとして格納できるようにします。

私がその間違いを持っ​​ていないと仮定すると、私が知りたいのは、エンディアンが異なる場合とそうでない場合がある2台のコンピューター間で情報を送信するときにエンディアンが主要な要因になるのはいつかということです。

  • 1の短整数をchar配列の形式で、修正なしで送信した場合、それは受信され、256として解釈されますか?

  • 次のコードを使用して短整数を分解して再構成すると、エンディアンはもはや要因になりませんか?

    // Sender:
    for(n=0, n < sizeof(uint16)*8; ++n) {
        stl_bitset[n] = (value >> n) & 1;
    };
    
    // Receiver:
    for(n=0, n < sizeof(uint16)*8; ++n) {
        value |= uint16(stl_bitset[n] & 1) << n;
    };
    
  • エンディアンを補う標準的な方法はありますか?

前もって感謝します!

70
Anne Quinn

非常に抽象的に言えば、エンディアンは変数をchar-arrayとして再解釈する特性です。

実際には、これは、外部バイトストリーム(ファイルやソケットなど)からread()write()する場合に正確に重要です。または、もう一度抽象的に言えば、エンディアンは次の場合に重要になりますserialize data(基本的に、シリアル化されたデータには型システムがなく、ダムバイトのみで構成されているため)。エンディアンはnot問題withinプログラミング言語です。言語はvaluesでのみ動作し、representationsでは動作しないためです。一方から他方に移動することは、詳細を掘り下げる必要がある場所です。

ウィットに-書く:

_uint32_t n = get_number();

unsigned char bytesLE[4] = { n, n >> 8, n >> 16, n >> 24 };  // little-endian order
unsigned char bytesBE[4] = { n >> 24, n >> 16, n >> 8, n };  // big-endian order

write(bytes..., 4);
_

ここでは、reinterpret_cast<unsigned char *>(&n)と言うことができ、結果はシステムのエンディアンに依存します。

そして読んで:

_unsigned char buf[4] = read_data();

uint32_t n_LE = buf[0] + buf[1] << 8 + buf[2] << 16 + buf[3] << 24; // little-endian
uint32_t n_BE = buf[3] + buf[2] << 8 + buf[1] << 16 + buf[0] << 24; // big-endian
_

ここでも、uint32_t n = *reinterpret_cast<uint32_t*>(buf)と言うことができ、結果はマシンのエンディアンに依存します。


ご覧のとおり、整数型では、代数の入出力演算を使用する場合、データストリームのみで、独自のシステムのエンディアンを知る必要はありません。 doubleなどの他のデータ型では、問題はより複雑になります。

50
Kerrek SB

ちなみに、デバイス間でデータを転送する場合は、ほとんどの場合、ntohlhtonlntohshtonsを使用したネットワークバイト順序を使用する必要があります。システムと宛先システムが何を使用しているかに関係なく、エンディアンのネットワークバイトオーダー標準に変換されます。もちろん、両方のシステムはこのようにプログラムする必要がありますが、通常はネットワークシナリオにあります。

  1. いいえ、あなたは正しい一般的な考えを持っていますが。欠落しているのは、通常はシリアル接続ですが、ネットワーク接続(少なくともほとんどのネットワーク接続)は、オクテット(バイト)レベルで正しいエンディアンを保証するという事実です。つまり、値を含むバイトを送信する場合です。リトルエンディアンマシンでは0x12の場合でも、ビッグエンディアンマシンでは0x12として受信されます。

    短い数字を見ると、16進数で数字を見ると、おそらく役に立ちます。それは0x0001として始まります。それを2バイトに分割します:0x000x01。受信すると、それは0x0100として読み取られ、256になります。

  2. ネットワークはオクテットレベルでエンディアンを処理するため、通常はバイトの順序のみを補正する必要があり、バイト内のビットは補正する必要はありません。

  3. おそらく最も簡単な方法は、送信時にhtons/htonlを使用し、受信時にntohs/ntohlを使用することです。それでも不十分な場合は、XDR、ASN.1、CORBA IIOP、Googleプロトコルバッファなど、多くの選択肢があります。

7
Jerry Coffin

どちらのエンディアネスにも、私が知っている利点があります。

  1. ビッグエンディアンは、位取り記数法に似ているため、概念的に理解しやすくなります。最も重要なものから最も重要でないものまでです。
  2. リトルエンディアンは、複数のメモリサイズのメモリ参照を再利用する場合に便利です。簡単に言えば、リトルエンディアンunsigned int*へのポインターがあり、そこに格納されている値が256未満であることがわかっている場合は、ポインターをunsigned char*にキャストできます。
6
amoss

補正の「標準的な方法」は、「ネットワークバイトオーダー」の概念が、ほとんどの場合(AFAIK)ビッグエンディアンとして定義されていることです。

送信者と受信者はどちらも有線プロトコルを知っており、必要に応じて送信前と受信後に変換して、アプリケーションに適切なデータを提供します。しかし、この変換は、アプリケーションではなく、ネットワーク層内で発生します。

6
Ray Toal

エンディアンは常に問題です。ネットワークに接続されているすべてのホストが同じOSなどを実行していることがわかっていれば、問題はないと言う人もいます。そうでないまで、これは真実です。オンワイヤデータの正確な形式を詳細に説明する仕様を常に公開する必要があります。任意の形式にすることができますが、すべてのエンドポイントが形式を理解し、正しく解釈できる必要があります。

一般に、プロトコルは数値にビッグエンディアンを使用しますが、すべての人がIEEE 754互換でない場合など、これには制限があります。オーバーヘッドを取ることができる場合は、XDR(またはお気に入りのソリューション)を使用して安全を確保してください。

C/C++エンディアンニュートラルコードのガイドラインを次に示します。明らかに、これらは「避けるべきルール」として書かれています...したがって、コードにこれらの「機能」がある場合、エンディアン関連のバグが発生する可能性があります。 (これは、ドブス博士に掲載されたエンディアンに関する私の記事からのものです)

  1. 異なるマルチバイトデータ型を組み合わせる共用体の使用は避けてください。 (ユニオンのレイアウトには、エンディアン関連の順序が異なる場合があります)

  2. バイトデータ型外のバイト配列にアクセスしないでください。 (バイト配列の順序にはエンディアン関連の順序があります)

  3. ビットフィールドとバイトマスクの使用は避けてください(ストレージのレイアウトはエンディアンに依存するため、バイトのマスキングとビットフィールドの選択はエンディアンに依存します)

  4. マルチバイト型から他のバイト型へのポインタのキャストは避けてください。
    (ポインターがあるタイプから別のタイプにキャストされると、ソース(つまり、元のターゲット)のエンディアンが失われ、後続の処理が正しくない可能性があります)

4
el rack

システムの境界にいない限り、心配する必要はありません。通常、stlの観点から話している場合は、すでにその境界を通過しています。

一連のバイトを、組み込み型であれカスタム型であれ、送信する型に変換する方法を示したり決定したりするのは、シリアル化プロトコルのタスクです。

組み込みのみを話している場合は、 環境によって提供されるツール ]によって提供されるマシン抽象化で十分な場合があります。

3
xtofl