エンディアンは、数値などの複数バイトからなるデータをメモリにどう並べるかの約束ごとです。
リトルエンディアンとビッグエンディアンの2種類があり、プログラム間や機器間でデータをやり取りする時に重要になります。
基本を押さえれば怖くありません。
意味と違い、見分け方と対処法を初心者向けにやさしく解説します。
エンディアン(リトル/ビッグ)とは?意味と基本
バイトオーダー(バイト順)の意味
コンピュータは整数や浮動小数点などを複数のバイト(8ビット)で表します。
この時、メモリの低いアドレスから高いアドレスへ、どのバイトをどの順に置くかという並べ方の規則をバイトオーダー(バイト順)と呼びます。
バイトオーダーは「値が変わる」のではなく「メモリ上の並び方が変わる」だけです。
例でイメージする
例えば16進数で0x1234という2バイトの値があるとします。
低いアドレス側に0x34が来るならリトルエンディアン、0x12が来るならビッグエンディアンです。
どちらの並べ方でも、CPUがその方式に従って読み書きすれば、値としては同じ0x1234になります。
リトルエンディアンとは
リトルエンディアンは、下位のバイト(最も小さい桁)から先に低いアドレスへ置く方式です。
0x12345678なら、低いアドレスから78 56 34 12の順に並びます。
現代のPCやスマートフォンで広く使われる一般的な方式がリトルエンディアンです。
ビッグエンディアンとは
ビッグエンディアンは、上位のバイト(最も大きい桁)から先に低いアドレスへ置く方式です。
0x12345678なら、低いアドレスから12 34 56 78の順に並びます。
「人が左から右へ桁を読む」感覚に近い並びで、ネットワークの標準(ネットワークバイトオーダー)でも採用されています。
プログラミングで気にする場面
テキスト(JSONやCSV)中心の処理ではほとんど意識しませんが、バイナリデータを保存・送受信・解析する場面ではエンディアンを必ず意識します。
典型的には、バイナリファイルの入出力、ソケット通信、他言語や他OSとのデータ交換、デバイスやセンサーからの生データ処理などです。
リトルエンディアンとビッグエンディアンの違い
4バイト整数の並び
4バイト整数0x12 34 56 78を、低いアドレスから順に並べると次のようになります。
違うのはメモリ上の順序だけで、数値としての意味はどちらも同じです。
| アドレスの昇順 | リトルエンディアン | ビッグエンディアン |
|---|---|---|
| 最も低い | 78 | 12 |
| 次 | 56 | 34 |
| 次 | 34 | 56 |
| 最も高い | 12 | 78 |
メモリ上の順序と数値の読み方
エンディアンは「どのバイトが先に置かれるか」を決めますが、プログラムがその環境の方式で読み書きする限り、printfやprintの結果は同じ数値になります。
一方で、メモリダンプやバイト配列を直接扱う場面では順序が違って見えるため、勘違いが起きやすいです。
Pythonならint.from_bytes(data, byteorder=”little”または”big”)のように、読み書き時に明示しておくと混乱を防げます。
どちらが主流か
現在の主流CPU(x86/x64、ARMの一般的なOS設定)はリトルエンディアンです。
ネットワークの標準はビッグエンディアンで、歴史的な機器や一部プロトコル/ファイル形式でもビッグが使われます。
日常のアプリ開発ではリトルが多い一方、通信や規格準拠では「ビッグ指定」に合わせるのが実務的です。
| 場面 | 典型的なエンディアン |
|---|---|
| PC/スマホのCPU | リトルエンディアン |
| ネットワーク(TCP/IP) | ビッグエンディアン(ネットワークバイトオーダー) |
| 一部のファイル形式(WAV/BMPなど) | 仕様で多くはリトル、例外もあり |
エンディアンの見分け方と対処
自分の環境のエンディアンの確認方法
確認は「言語で調べる」か「OSのコマンドで調べる」方法が簡単です。
どちらでも可能な方法を1つ覚えておけば十分です。
- Python
import sys
print(sys.byteorder) # 'little' または 'big'
- Node.js
console.log(require('os').endianness()); // 'LE' または 'BE'
- C(C99以降の例)
#include <stdio.h>
#include <stdint.h>
int main(void) {
union { uint32_t i; uint8_t b[4]; } u = { .i = 0x01020304 };
if (u.b[0] == 0x04) puts("little");
else puts("big");
return 0;
}
- Linux
lscpu | grep -i 'byte order' # 例: Little Endian
複数方法で同じ結果が出れば安心です。
バイナリファイル入出力の注意点
バイナリはテキストと違って人間の目で中身が読めません。
だからこそ「ファイル形式の仕様でエンディアンを明記し、読み書きで必ずその通りに変換する」ことが重要です。
言語やCPUに任せてメモリをそのまま書き出す(fwriteで構造体を丸ごと保存など)と、別の環境で読み取れない恐れがあります。
仕様がないまま何となく保存しないのが安全です。
ネットワークバイトオーダー(ビッグ)への対応
ネットワークはビッグエンディアンが標準です。
送受信時は「ホスト(自分の環境)のエンディアン」と「ネットワーク(ビッグ)」の間で変換するのが基本です。
- Cのソケット関数
- 送信前: htons/htonl、受信後: ntohs/ntohl
- Pythonのstruct
- パック: struct.pack(‘!I’, 値) 、アンパック: struct.unpack(‘!I’, データ)
- ‘!’はネットワーク(ビッグ)指定、'<‘はリトル、’>’はビッグ
- Node.js(Buffer)
- 書き込み: buf.writeUInt32BE(value, offset)、読み出し: buf.readUInt32BE(offset)
プロトコルが「ビッグで送る」と決めているなら、常にBE系のAPIを使います。
エンディアン変換の考え方
変換は「ビット」ではなくバイトの並べ替えです。
32ビットなら4バイトを逆順に並べればよいだけです。
自分でビット演算を書くより、言語や標準ライブラリの関数を使う方が安全で読みやすいです。
- Pythonでの変換イメージ
x = 0x12345678
b = x.to_bytes(4, byteorder="little")
y = int.from_bytes(b, byteorder="big") # リトル→ビッグへ並べ替え
- Cでの簡単なスワップ例(32ビット)
#include <stdint.h>
uint32_t bswap32(uint32_t x){
return ((x & 0x000000FFU) << 24) |
((x & 0x0000FF00U) << 8) |
((x & 0x00FF0000U) >> 8) |
((x & 0xFF000000U) >> 24);
}
符号付き整数でのシフトは思わぬ挙動になる場合があるため、変換は符号なし型で行うのが無難です。
ライブラリやAPIの指定に合わせる
多くのAPIはエンディアンを引数やフォーマット文字で指定できます。
「この関数はLE/BEどちらを前提にしているか」をドキュメントで確認し、合わせて使うだけで事故は大きく減ります。
Pythonのstruct(‘<I’や’!I’)、Node.jsのBuffer(‘…LE’と’…BE’)、JavaのByteBuffer.order(ByteOrder.LITTLE_ENDIAN)など、明示できるツールを選ぶと安心です。
初心者向けの例とよくあるつまずき
1バイトデータはエンディアンの影響なし
エンディアンはバイトの順序を決める話なので、1バイトのデータ(uint8/byte/charなど)には影響しません。
画像の各チャンネル1バイトや、フラグの1バイトなどでは気にしなくてよい場面が多いです。
異なる環境でのデータ交換の落とし穴
同じ構造体を、A環境ではリトルのままfwriteし、B環境ではビッグのままfreadすると、値のバイトが入れ替わって誤解釈されます。
例えば0x00000001が0x01000000として読まれるなどです。
「同じC言語だから大丈夫」とは限らないので、必ずエンディアンを固定して書き、相手側でその方式で読む手順を入れましょう。
データ形式を決めて保存するコツ
保存する前に、形式を短くても良いので文章で決めておくと後が楽になります。
例えば「先頭4バイトはリトルの符号なし整数、次の2バイトはビッグの長さ…」のように、各フィールドの長さ・型・エンディアンを列挙します。
テキストのJSONにする場合でも、数値の桁数やエンコード(UTF-8など)を明記しておくと、言語が違っても安定して扱えます。
小さな値でテストして確認する
テストは0x00000001や0x01020304のような「並びが分かりやすい値」を使うと良いです。
送る側でバイト配列を16進表示し、受ける側で同じ配列になるかを確認すれば、エンディアンのズレにすぐ気づけます。
Pythonならbytes.hex()、Node.jsならbuf.toString(‘hex’)、Cならprintfで各バイトを%02Xで表示すると見やすいです。
まとめ
エンディアンは「複数バイトの並べ方」の規則で、リトルは下位バイト優先、ビッグは上位バイト優先です。
普段は意識しなくても動きますが、バイナリの保存や通信、異なる環境とのやり取りでは必須の知識になります。
自分の環境の方式を把握し、ファイルやプロトコルの仕様に合わせてエンディアンを明示することが実務のコツです。
ネットワークはビッグが標準で、PC/スマホはリトルが主流という全体像を覚えておくと迷いません。
「明示して変換する」を基本に、シンプルなテストで確認しながら安心して扱っていきましょう。
