次の種類のエンディアンの違いは何ですか?
- バイト (8b) 不変のビッグ エンディアンとリトル エンディアン
- ハーフワード (16b) 不変のビッグエンディアンとリトルエンディアン
- 単語 (32b) 不変のビッグ エンディアンとリトル エンディアン
- ダブルワード (64b) 不変のビッグエンディアンとリトルエンディアン
他のタイプ/バリエーションはありますか?
次の種類のエンディアンの違いは何ですか?
他のタイプ/バリエーションはありますか?
エンディアン マッピングには、アドレス不変性とデータ不変性の 2 つのアプローチがあります。
このタイプのマッピングでは、バイトのアドレスは常に大きなものと小さなものの間で保持されます。これには、特定のデータ (2 バイトまたは 4 バイトのワードなど) の重要度の順序 (最上位から最下位) が逆になり、データの解釈が逆になるという副作用があります。具体的には、リトル エンディアンでは、データの解釈は最下位バイトから最上位バイトへと行われますが、ビッグ エンディアンでは、解釈は最上位バイトから最下位バイトへと行われます。どちらの場合も、アクセスされるバイトのセットは同じままです。
例
アドレスの不変性 (バイトの不変性とも呼ばれます): バイト アドレスは一定ですが、バイトの意味が逆になります。
Addr Memory
7 0
| | (LE) (BE)
|----|
+0 | aa | lsb msb
|----|
+1 | bb | : :
|----|
+2 | cc | : :
|----|
+3 | dd | msb lsb
|----|
| |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0xaa (preserved)
Read 2 bytes: 0xbbaa 0xaabb
Read 4 bytes: 0xddccbbaa 0xaabbccdd
このタイプのマッピングでは、特定のサイズのデータに対して相対的なバイトの重要度が保持されます。したがって、さまざまなデータ サイズに対してさまざまなタイプのデータ不変エンディアン マッピングがあります。たとえば、32 ビット ワードの不変エンディアン マッピングは、32 のデータ サイズに使用されます。特定のサイズのデータの値を保持する効果は、データ内のバイトのバイト アドレスが、ビッグ エンディアン マッピングとリトル エンディアン マッピングの間で逆になることです。 .
例
32 ビット データの不変性 (ワードの不変性0xddccbbaa
とも呼ばれます): データは、エンディアンに関係なく常に値を持つ 32 ビットのワードです。ただし、1 ワードより小さいアクセスの場合、ビッグ エンディアン マッピングとリトル エンディアン マッピングの間でバイトのアドレスが逆になります。
Addr Memory
| +3 +2 +1 +0 | <- LE
|-------------------|
+0 msb | dd | cc | bb | aa | lsb
|-------------------|
+4 msb | 99 | 88 | 77 | 66 | lsb
|-------------------|
BE -> | +0 +1 +2 +3 |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0xdd
Read 2 bytes: 0xbbaa 0xddcc
Read 4 bytes: 0xddccbbaa 0xddccbbaa (preserved)
Read 8 bytes: 0x99887766ddccbbaa 0x99887766ddccbbaa (preserved)
例
16 ビット データの不変性 (ハーフ ワードの不変性0xbbaa
とも呼ばれます): データは、エンディアンに関係なく常に値を持つ 16 ビットです。ただし、ハーフ ワードより小さいアクセスの場合、ビッグ エンディアン マッピングとリトル エンディアン マッピングの間でバイトのアドレスが逆になります。
Addr Memory
| +1 +0 | <- LE
|---------|
+0 msb | bb | aa | lsb
|---------|
+2 msb | dd | cc | lsb
|---------|
+4 msb | 77 | 66 | lsb
|---------|
+6 msb | 99 | 88 | lsb
|---------|
BE -> | +0 +1 |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0xbb
Read 2 bytes: 0xbbaa 0xbbaa (preserved)
Read 4 bytes: 0xddccbbaa 0xddccbbaa (preserved)
Read 8 bytes: 0x99887766ddccbbaa 0x99887766ddccbbaa (preserved)
例
64 ビット データの不変性 (ダブル ワードの不変性0x99887766ddccbbaa
とも呼ばれます): データは、エンディアンに関係なく常に値を持つ 64 ビット ワードです。ただし、ダブル ワードより小さいアクセスの場合、ビッグ エンディアン マッピングとリトル エンディアン マッピングの間でバイトのアドレスが逆になります。
Addr Memory
| +7 +6 +5 +4 +3 +2 +1 +0 | <- LE
|---------------------------------------|
+0 msb | 99 | 88 | 77 | 66 | dd | cc | bb | aa | lsb
|---------------------------------------|
BE -> | +0 +1 +2 +3 +4 +5 +6 +7 |
At Addr=0: Little-endian Big-endian
Read 1 byte: 0xaa 0x99
Read 2 bytes: 0xbbaa 0x9988
Read 4 bytes: 0xddccbbaa 0x99887766
Read 8 bytes: 0x99887766ddccbbaa 0x99887766ddccbbaa (preserved)
フィリバートは言った、
ビットは実際に反転されました
アーキテクチャがバイト値の不変性を破るとは思えません。ビットフィールドを含む構造体をデータに対してマッピングする場合、ビットフィールドの順序を反転する必要がある場合があります。このような直接マッピングは、C99 標準の外側にあるコンパイラの仕様に依存していますが、まだ一般的である可能性があります。ダイレクト マッピングは高速ですが、パッキング、アラインメント、およびバイト オーダーを規定しない C99 標準には準拠していません。C99 準拠のコードでは、アドレスではなく値に基づくスロー マッピングを使用する必要があります。つまり、これを行う代わりに、
#if LITTLE_ENDIAN
struct breakdown_t {
int least_significant_bit: 1;
int middle_bits: 10;
int most_significant_bits: 21;
};
#elif BIG_ENDIAN
struct breakdown_t {
int most_significant_bits: 21;
int middle_bits: 10;
int least_significant_bit: 1;
};
#else
#error Huh
#endif
uint32_t data = ...;
struct breakdown_t *b = (struct breakdown_t *)&data;
これを書く必要があります(これは、コンパイラが上記の「直接マッピング」に対してもコードを生成する方法です)、
uint32_t data = ...;
uint32_t least_significant_bit = data & 0x00000001;
uint32_t middle_bits = (data >> 1) & 0x000003FF;
uint32_t most_significant_bits = (data >> 11) & 0x001fffff;
各エンディアンニュートラルなアプリケーション固有のデータストレージユニットでビットフィールドの順序を逆にする必要がある理由は、コンパイラがビットフィールドを成長するアドレスのバイトにパックするためです。
各バイトの「ビットの順序」は重要ではありません。それらを抽出する唯一の方法は、値のマスクを適用し、最下位ビットまたは最上位ビットの方向にシフトすることです。「ビットの順序」の問題は、ビットアドレスの概念を持つ架空のアーキテクチャでのみ重要になります。私は、すべての既存のアーキテクチャがこの概念をハードウェアに隠し、エンディアン中立のバイト値に基づく概念である最下位ビットと最上位ビットの抽出のみを提供していると考えています。
ミドルエンディアンまたは混合エンディアンもあります。詳細については、ウィキペディアを参照してください。
私がこれについて心配しなければならなかったのは、C でネットワーク コードを記述するときだけでした。ネットワークでは通常、ビッグ エンディアンの IIRC が使用されます。ほとんどの言語は、すべてを抽象化するか、適切なエンディアンを使用していることを保証するライブラリを提供します。
エンディアンについて読んだ最高の記事「ビッグエンディアンとリトルエンディアンのバイトオーダーを理解する」。
実際には、マシンのエンディアンをビットの順序ではなく、ワード内のバイトの順序として説明します。
そこまでの「バイト」とは、「アーキテクチャが個別に管理できるメモリの最小単位」を意味します。したがって、最小単位が 16 ビット長 (x86 ではwordと呼ばれる) の場合、値 0xFFFF0000 を表す 32 ビットの「単語」は次のように格納できます。
FFFF 0000
またはこれ:
0000 FFFF
エンディアンに応じてメモリ内。
したがって、エンディアンが 8 ビットの場合、16 ビットで構成されるすべてのワードが次のように格納されることを意味します。
FF 00
また:
00 FF
等々。
実際には、エンディアンとは、プロセッサが特定のメモリ位置のコンテンツを解釈する方法を指します。たとえば、次の内容(16進バイト)のメモリ位置0x100がある場合
0x100: 12 34 56 78 90 ab cd ef
Reads Little Endian Big Endian
8-bit: 12 12
16-bit: 34 12 12 34
32-bit: 78 56 34 12 12 34 56 78
64-bit: ef cd ab 90 78 56 34 12 12 34 56 78 90 ab cd ef
エンディアンを気にする必要がある2つの状況は、ネットワークコードの場合と、ポインターを使用してダウンキャストを行う場合です。
TCP / IPは、ネットワーク上のデータがビッグエンディアンであることを指定しています。バイト配列以外の型(構造体へのポインターなど)を送信する場合は、データがビッグエンディアンで送信されるように、必ずntoh/htonマクロを使用する必要があります。リトルエンディアンプロセッサからビッグエンディアンプロセッサに(またはその逆に)送信すると、データが文字化けします...
キャストの問題:
uint32_t* lptr = 0x100;
uint16_t data;
*lptr = 0x0000FFFF
data = *((uint16_t*)lptr);
データの価値はどうなりますか?ビッグエンディアンシステムでは0になりますリトルエンディアンシステムではFFFFになります
13年前、私はDECALPHAシステムとPCの両方に移植可能なツールに取り組みました。このDECALPHAでは、ビットは実際に反転されていました。あれは:
1010 0011
実際に翻訳された
1100 0101
私がビットフィールドを次のように宣言したことを除いて、Cコードではほとんど透過的でシームレスでした
typedef struct {
int firstbit:1;
int middlebits:10;
int lastbits:21;
};
これを変換する必要がありました(#ifdef条件付きコンパイルを使用)
typedef struct {
int lastbits:21;
int middlebits:10;
int firstbit:1;
};
基本的な概念はビットの順序付けです:
1010 0011
リトルエンディアンでは
0011 1010
ビッグエンディアンで(およびその逆)。
個々のビットではなく、グループ化によって順序が変わることに気付くでしょう。たとえば、どこのシステムかわかりません
1100 0101
最初のバージョンの「別のエンディアン」バージョンになります。