エンディアンの違いと対策

エンディアンは複数バイトデータの並び順を定義する重要な概念であり、通信やデータ保存においてエラーの原因となりやすいポイントです。この記事では基本的な仕組みから、変換方法や実装時の注意点を解説します。

エンディアンとは

ビッグエンディアンとリトルエンディアンでメモリ上のバイトの並びが逆になることを示した図。上段のビッグエンディアン(最上位バイトが先)は、低位アドレスから12・34・56・78の順に並ぶ。採用例はTCP/IP通信プロトコル。下段のリトルエンディアン(最下位バイトが先)は、低位アドレスから78・56・34・12の順に並ぶ。採用例はWindows OSやUSB(ディスクリプタ等)。

エンディアンとは複数バイトで構成されるデータをメモリに並べる際やネットワーク経由で通信する際のバイトオーダー(バイト順)の規則です。メモリやネットワーク上ではデータが1バイト単位で管理されているため、複数バイトの並び順を定義する必要があります。

エンディアンには主にビッグエンディアンとリトルエンディアンの2つの方式があり、どちらを使用しているかでデータを格納する順番が変わります。誤ったエンディアンで解釈するとまったく異なる値になってしまうため、処理において重大なエラーを引き起こす可能性があります。

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

ビッグエンディアンでは最上位バイトが先に格納されます。TCP/IP(Internetプロトコル群)では、パケットヘッダ中の多バイト整数フィールドはネットワークバイトオーダー(ビッグエンディアン)で表現されます。リトルエンディアンでは最下位バイトが先に格納されます。一般的なPC向けプロセッサで採用されています。またUSBでは、ディスクリプタ等の多バイトフィールドがリトルエンディアンで規定されています。

どちらを使用するかは、通信プロトコルやファイルフォーマット、インターフェース仕様などであらかじめ定義されており、一致しない場合は変換が必要です。

一部のプロセッサは実行モードや設定によりビッグエンディアンとリトルエンディアンを切り換え可能なバイエンディアンに対応しており、ハードウェアレベルでバイトオーダーを切り換えられます。(CPUがバイエンディアン対応でも)製品仕様としてのデータ表現(通信/保存フォーマット)のエンディアンは固定し、必要に応じて変換層で吸収するのが一般的です。

通信・データ保存の落とし穴

データ送信側と受信側でエンディアンが異なるとき、同じバイト列でも解釈結果が変わってしまいます。特に通信処理はエンディアンの影響を受けやすい処理です。そのため、通信プロトコルではあらかじめバイトオーダーが規定されています。

たとえば、TCP/IPで採用されるネットワークバイトオーダーではビッグエンディアンに統一されています。

画像ファイルなどのバイナリデータを転送する場合はバイトオーダーを定義して混同を避けます。

また、UTF-16などでは、ストリーム/ファイルの先頭にBOM(Byte Order Mark)を付けてエンディアンを示す場合があります。

エンディアン変換

異なる機器の間でデータを正しく扱うにはエンディアン変換が必要です。変換関数は用途別に、(1)ネットワークバイトオーダー用(hton*/ntoh*)と、(2) 任意のエンディアン指定用(htole*/htobe*、le*toh/be*toh)に分けられます。

ネットワーク系関数にはhtons・htonl(ホスト→ネットワーク変換)、ntohs・ntohl(ネットワーク→ホスト変換)などがあります。たとえばhtonsは16ビット値を、htonlは32ビット値をそれぞれホストバイト順からネットワークバイト順へ変換します。

バイトオーダー変換関数としてはhtole32、be32toh(Linux/BSD環境)などが使われ、ファイルやセンサデータなど任意のデータ変換用です。Linuxでは主に<endian.h>、BSD系では<sys/endian.h>等で提供されます(環境差あり)。htole32はシステムのネイティブなバイトオーダーからリトルエンディアンのバイトオーダーに変換します。be32tohはビッグエンディアンのバイトオーダーをシステムのネイティブなバイトオーダーに変換します。

なお、これらの関数はOS/Cライブラリ依存です。組み込み環境(ベアメタルや独自libc)では未提供の場合があるため、利用可能なヘッダ(endian.h/sys/endian.h等)を確認してください。環境によっては独自マクロやビットシフト演算で実装します。

ネイティブなバイトオーダーと変換先が一致している場合、結果として変換不要なこともあります。ただしコード上で意図を明確にできるため、「仕様としてどちらの並びを期待するか」を示す目的で明示的に変換関数を使うと可読性が上がります。

実装時の注意点

エンディアンの違いはメモリ読み込み時やデータ保存時に不具合の原因となります。設計段階から注意しておくと、後の工程で予期しないエラーを防げます。

設計指針をそろえる

エンディアンが混在する場合、どこでエンディアンの違いを吸収するか検討しなければなりません。通信処理ではプロトコルに従ってビッグエンディアンで扱い、システム内部ではネイティブのエンディアンで処理するなど、ルールを決めておくと責任範囲が明確になり、たとえ混在したとしても変更箇所を特定しやすくなります。

また、エンディアンが問題になるのはバイナリデータを扱う場合です。JSONやCSVなどの文字列データとして保存することで、エンディアン依存は回避できますが、サイズやデータ転送速度とトレードオフになります。

不揮発性メモリ実装時の注意点

異なるCPUで読み出すことを前提にした不揮発性メモリへの実装時は、保存時のエンディアンを統一することで、読み込みと書き込みのエラーを防ぎます。メモリへの読み書き時に明示的な変換処理を挟む、フォーマット識別子を付与するなど、エンディアンを明確な状態にしておけば、バイトオーダー不一致を防げます。

組み込みソフトの世界 トップへ戻る