はじめに
ネットワーク通信やバイナリファイルの読み書きなど、異なるシステム間でデータをやり取りする際、**エンディアン(バイトオーダー)**の違いが問題になることがあります。エンディアンとは、複数のバイトで構成されるデータをメモリ上に並べる順序のことで、主に以下の2種類があります。
- リトルエンディアン: 下位バイトをメモリの小さいアドレスに格納する方式。(例: Intel x86系CPU)
- ビッグエンディアン: 上位バイトをメモリの小さいアドレスに格納する方式。(例: ネットワークプロトコル)
C++20では、プログラムを実行している環境のエンディアンを判定し、またエンディアンを変換(バイトスワップ)するための、標準的で移植性の高いツールが導入されました。
この記事では、<bit>
ヘッダーで提供される std::endian
と std::byteswap
の使い方を解説します。
【前提】C++20とは?
C++20(シーピープラスにーぜろ)は、2020年に正式化されたC++言語のメジャーアップデート版です。この記事で紹介する機能はC++20で導入されたため、利用するにはC++20に対応したコンパイラが必要です。
エンディアンの判定と変換のサンプルコード
このコードは、まず現在の実行環境がリトルエンディアンかビッグエンディアンかを判定し、その後、byteswap
関数を使っていくつかの数値のバイトオーダーを反転させ、その結果を表示します。
完成コード
#include <iostream>
#include <bit> // std::endian, std::byteswap
#include <cstdint> // uint16_t, uint32_t, uint64_t
using namespace std;
int main() {
// 1. 現在の環境のエンディアンを判定
if constexpr (endian::native == endian::little) {
cout << "現在の環境は: リトルエンディアンです。" << endl;
} else if constexpr (endian::native == endian::big) {
cout << "現在の環境は: ビッグエンディアンです。" << endl;
} else {
cout << "現在の環境は: その他のエンディアンです。" << endl;
}
cout << "\n--- バイトスワップの実行例 ---" << endl;
// 2. byteswapでバイトオーダーを変換
uint16_t val16 = 0xABCD; // 2バイト整数
uint32_t val32 = 0x1234ABCD; // 4バイト整数
cout << hex << showbase; // 16進数表示モード (0xプレフィックス付き)
cout << val16 << " => " << byteswap(val16) << endl;
cout << val32 << " => " << byteswap(val32) << endl;
return 0;
}
実行結果(リトルエンディアン環境の場合)
現在の環境は: リトルエンディアンです。
--- バイトスワップの実行例 ---
0xabcd => 0xcdab
0x1234abcd => 0xcdab3412
コードの解説
1. エンディアンの判定 (std::endian
)
if constexpr (endian::native == endian::little)
std::endian
:<bit>
ヘッダーで定義されている列挙型で、little
(リトルエンディアン),big
(ビッグエンディアン),native
(処理系依存)の3つの列挙子を持ちます。endian::native
: コンパイルしている環境のエンディアンを表します。if constexpr
: コンパイル時に条件分岐を評価します。これにより、実行環境に依存するコードを、実行時コストゼロで最適化できます。
2. バイトオーダーの変換 (std::byteswap
)
byteswap(val16)
std::byteswap
:<bit>
ヘッダーで提供される関数で、引数として渡された整数値のバイトの並びを完全に逆転させた値を返します。- 例:
0xABCD
という2バイトのデータは、メモリ上ではAB
CD
というバイトの並びです。byteswap
はこれをCD
AB
という並びに反転させ、0xCDAB
という値にします。 0x1234ABCD
という4バイトのデータは、12
34
AB
CD
というバイトの並びから、CD
AB
34
12
という並びに反転され、0xCDAB3412
となります。
まとめ
今回は、C++20で導入された std::endian
と std::byteswap
を使って、エンディアンの判定と変換を行う方法を解説しました。
std::endian::native
: 実行環境のエンディアンをコンパイル時に判定できる。std::byteswap(value)
: 整数のバイトオーダーを安全かつ効率的に反転できる。
これらの標準機能を使うことで、従来は自前で複雑な実装が必要だったバイトオーダー関連の処理を、移植性が高く、簡潔なコードで記述できるようになります。