エンディアン問題

ファイルの読み書きでしばしば問題となるのが,このビッグエンディアンとリトルエンディアンである.特に従来Macで使われることが主であったアプリケーションが書き出したファイルは,ビッグエンディアンで出力されていることが多く,そういったファイルをWindows環境で読み取ると問題になるといったことが往々にしてある.また,最近ではWindowsが主環境となっているため,その逆もまたしかりである.
エンディアン問題とは,メモリ上データのバイト単位での配置の並び順に起因するものである.具体的に,ビッグエンディアンとは4バイトのデータがある場合,そのデータを上位からメモリ上に書き込んで行くものである.逆にリトルエンディアンは,データを下位から書き込んでいるものである.
これは,OS上の問題とも思われがちであるが,実はCPUの挙動に関する問題であり,エンディアンは,CPUによって決定される.MOTOROLAのCPUは,ビッグエンディアンであり,インテルのCPUは,リトルエンディアンとなる.例えば,MOTOROLAのG4で起動するWindowsがもしあるとすれば,それはビッグエンディアンで駆動することになる(CPUの挙動自体をまねして動くものであるため,エミュレータはその限りではない.).ここでは,ビッグエンディアンは,MOTOROLAのCPUが主に使われるMac環境,リトルエンディアンは,インテルのCPUが主に使われるWindows環境として,取り扱うこととする.また,頭に0xを付けた場合は16進数を意味し,付けない場合は10進数を意味することとする.

ビッグエンディアン

ビッグエンディアンで動作するCPUでは,データをバイトを上位から書き込んで行くものである.例えば,2バイトのshort型の変数aに20,040の値を代入した場合,概念的に16進数で,0x4E48として変数に保存されると考えることができる.
コンピュータ,つまりCPUはデータをすべて,2進数つまり0か1で保存する.近年のCPUは,このデータを2進数が8個集まったバイト単位で管理することが一般的である.つまり,変数aのデータは,0x4Eと0x48に分けて管理されることになる.ビッグエンディアンのCPUでは,このデータを0x4E,0x48の順にメモリ,ファイル等に保存する.つまり,どんな変数つまりデータ型であってもすべてバイト単位に切り分けられ,すべて管理されるのである.例えば,int型の変数に2,094,967,290という値を代入した場合,データは,0x7C,0xDE,0xA9,0xFAの4つのバイトに分けて管理され,ビッグエンディアンのCPUである場合,メモリ上には0x7C,0xDE,0xA9,0xFAの順で保存されることになる.


エンディアンは,バイト単位で管理するところに発生する問題であるため,char型のような1バイトのデータ型の場合,問題点とはならない.

リトルエンディアン

リトルエンディアンは,ビッグエンディアンの逆である.つまり,データをすべて下位バイトかメモリやファイルに保存する.リトルエンディアンのCPUで,int型の変数に2,094,967,290という値を代入した場合,メモリ上には0xFA,0xA9,0xDE,0x7Cの順で保存されることになる.



起こりうる問題と解決方法とBitmapファイル

エンディアン問題がはらむ危険性の例として,リトルエンディアンで書き出したファイルをビッグエンディアンで読み取るとき,またネットワーク上でパケットデータを交換したときのデータの不整合がある.例えば,下図のようにファイルに書き出しデータをエンディアン問題を考慮せずに2種類のCPU間で共有すると,入力と出力が異なるプログラムの流れになってしまうのである.


この問題の解決方法としては,どちらかの環境での読み書き時にバイト並びをうまく,並び替えてやればよいのである.例えば,C言語でのコーディングならばこうなる.

unsigned short SwapShortByteOrder(unsigned short n){
return (n << 8) | (n >> 8);
}
unsigned int SwapIntByteOrder(unsigned int n){
return SwapShortByteOrder(n >> 16) | (SwapShortByteOrder(n) << 16);
}

これが並び替えのコードとなる(BBSに書き込んで頂いた2さんに謝辞を申し上げる,sonsonのコードよりは信頼性は高いかと).簡単に言うと, SwapShortByteOrderでは,<<と>> で8ビット元の値をずらし,論理演算の和をとって,バイトの並びを置き換えているのである.また,SwapIntByteOrderは,再帰的にそれを行っているだけである.
前節のBitmapファイルの読み書きの関数をMacOSX等の環境へ移植する場合について簡単に説明する.Bitmapファイルは,ビッグエンディアンで読み書きする仕様であるため,それに従い処理を行うこととなる.SwapIntByteOrder,およびSwapShortByteOrderの2つの処理を2byte以上のファイルの読み書きを実行する部分に,追加することで対処できる.