キーワード

ビッグエンディアン, リトルエンディアン


これだけは覚えよう


原因と原理

アライメントやワード境界の話と同様、メモリのバイトオーダの話はプロセッサとメモリの関係によって生じます。

今日のプロセッサの多くはバイトアドレッシング方式と呼ばれる方式でメモリをアクセスしに行きます。この方式は、メモリのデータ幅(箱の大きさ)に関係なく、1バイト単位でアドレスをつけていく方式です。1バイトごとにアドレスをつけていくので、メモリの容量はアドレス空間の大きさと同じです。

[余談] バイトアドレッシング方式と対になる方式がワードアドレッシング方式です。これは、メモリのデータ幅にあわせ、メモリの箱単位でアドレスをつけていく方式です。メモリのアドレスバス上に流れる値(SDRAMの場合、RAS + CAS)とアドレスが一致するの特徴で、メモリの容量はデータ幅 x アドレス空間になります。

プログラム中では、数値演算はレジスタ長を単位として行われます。32ビットプロセッサなら、一度に32ビットの数値が取り扱います。これは、多くのプロセッサは数値演算を行うとき、レジスタを用いるためです(Load/Store architecture)。一方で、バイトアドレッシング方式の場合 データの格納単位はバイトなので、レジスタの内容をメモリに書き出すときにはバイト単位で行います。すると、次の問題が起こります。

「32ビットは4バイトあるけど、どの順番でメモリに書けばいいの?」

このとき、「多バイトで構成されるデータをどの順番で格納するか」というのがバイトオーダです。そのため、8ビットメモリと8ビットプロセッサを組み合わせた場合や、ワードアドレッシング方式のプロセッサの場合、バイトオーダはありません(厳密にはウソ)。近年、多くのプロセッサはバイトアドレッシング方式の32ビットプロセッサであるため、バイトオーダの問題はほぼ避けられない問題だと思ってよいでしょう。

ちなみに、バイトオーダのことをエンディアンと呼んだりもします。


ビッグエンディアン と リトルエンディアン

バイトオーダは、格納の順序によって次の2種類に分類されます。

バイトオーダの違い

なぜバイトオーダのことをビッグエンディアンとかリトルエンディアンと呼ぶかは本質外なので割愛します。知りたい方は「卵」「割る」「エンディアン」で検索すると良いでしょう。またリトルエンディアンとビッグエンディアンのどちらが優れているという話も、宗教戦争になるので割愛します。誰も最初に使っているプロセッサのエンディアンに惹かれるので、私ならリトルなのですが、m68kユーザはビッグが常識だそうです。なので事実だけを述べたいと思います。

バイトオーダの違いによって、プログラムの実行速度も変化します。たとえば、C言語でuint64_t(64ビット符号なし整数)の変数をuint32_t(32ビット符号なし整数)にキャストするときのことを考えます。C言語で(uint32_t)(0x0123456789ABCDEFllu)を実行すると、下の部分 0x89ABCDEFが出てきます。もしプロセッサのバイトオーダがリトルエンディアンである場合、キャストは読み出すデータの長さを変えるだけで実現できます。今回の場合では、32ビット分に相当する4バイトだけ読み込んでくれば、uint32_tでキャストしたのと同じ結果を得ることができます。それは、リトルエンディアンの場合は最下位バイトから順に並んでいるためです。

一方、ビッグエンディアンの場合にはそうは行きません。なぜなら、ビッグエンディアンは最上位バイトから並んでいるため、キャストと同じ効果を得るにはいくらかのオフセットをつけて読み出さなければいけません。この例の場合だと、+4バイトずれた位置から読み出さないと正しい結果になりません。SuperHやARMのような1ステートのオフセット付レジスタ間接アドレッシング転送命令を持ったプロセッサなら良いですが、オフセット付転送命令が通常の転送命令よりも遅いプロセッサや、元々そのような命令の無いプロセッサの場合、オフセットの計算のため多少のオーバヘッドが生じます。

バイトオーダが違うとキャストも変わる
(&を便宜上 char * を返却するものとする)

そうなるとリトルの方が... と言う気もしますが、ビッグエンディアンの場合、その特徴を利用すればこういう使い方もできます。あるuint64_t型の整数をuint16_tで表現可能な範囲で丸めたいという場合、最上位から2バイト分を読み出す必要があります。この場合は先ほどの例とは逆になり、リトルエンディアンの場合はオフセット演算をする必要がありますが、ビッグエンディアンならそのまま読み出すだけで小さい型への丸め処理が実現できます。

処理の内容リトルエンディアンビッグエンディアン
ダンプ結果逆順そのまま読める
異なるサイズの型へのキャストアドレスが同一型に応じて補正が要る
小さい型への丸め型に応じて補正そのまま読み出すだけ
データ長の拡張割と自然にできるデータをずらす必要がある

プロセッサ以外のバイトオーダ

「多バイトのデータをバイト配列に格納する」という作業をする場合、常にバイトオーダを決定する必要があります。そのため、プロセッサの外でもバイトオーダは出てきます。

現状で一番良く知られているのは、「ネットワークバイトオーダ」です。TCP/IPなどの通信プロトコルはバイト配列を扱いますが、MACやIPアドレスといったデータは多バイトデータですので、通信路に出力する順番を決める必要があります。ちなみにTCP/IPのネットワークバイトオーダはビッグエンディアンです。その他RFC上の多くのプロトコルもビッグエンディアンでデータをやり取りしています。