※修正前があまりにもひどかったため、指摘事項の反映もかねて書き直しました。以前の記事にコメントくださった方すいません。
はじまり
MIDIファイルを読み込み、memcpy()でデータを変数にコピー。
しかしなぜか値が合わない。デバッガでメモリを覗くと順番が変…。
そういえばエンディアンがどうとか聞いたな…。
環境
- windows11(intel core i CPUを使用)
検証コード
#include<cstdio>
#include<cstdint>
int main(void) {
uint32_t num = 0x12345678;
uint8_t* p_num =(uint8_t*)#
printf("uint32_t*:%p 値%#x\n", &num, num);
for (int i = 0; i < 4; i++) {
printf("uint8_t* :%p 値%#x\n", &p_num[i], p_num[i]);
}
}
<出力>
uint32_t* :000000E158AFF934 値0x12345678
uint8_t[0] :000000E158AFF934 値0x78
uint8_t[1] :000000E158AFF935 値0x56
uint8_t[2] :000000E158AFF936 値0x34
uint8_t[3] :000000E158AFF937 値0x12
8bitづつ区切って下位ビットから順に置かれている。
図にするとこんな感じ。てっきり上位ビットから置かれているもんだと思ってた。
原因
MIDIファイル(ビッグエンディアン)のファイルから、複数バイトのデータを何も考えずにmemcpy()
していたこと。
使用している環境はリトルエンディアンのため、意図しない値となる。(例えば、MIDIファイルから読み取った0x12
,0x34
というデータをuint16_t型
の変数にそのままmemcpy()
すると、0x3412となる。)
対策
こんな関数をつくってみた。おそらくもっと良い方法がある。
/// <summary>
/// エンディアン変換
/// </summary>
/// <typeparam name="TYPE"></typeparam>
/// <param name="data">変換する変数のポインタ(変換先の型のポインタでキャストすること)</param>
/// <returns></returns>
template<typename TYPE> _inline endian_change(TYPE* data) {
TYPE num = 0;
uint8_t* p_buf = (uint8_t*)data;//uint8_tに戻す
for (int i = 0; i < sizeof(TYPE); i++) {
num += p_buf[i] << (8 * (sizeof(TYPE) - 1 - i));
}
return num;
}
まとめ
メモリを細かく制御するときはエンディアンを意識しよう
ビッグエンディアンに統一してくれないかな