LoginSignup
1
1

その変数、メモリにどう置かれているか意識してますか?

Last updated at Posted at 2023-12-23

※修正前があまりにもひどかったため、指摘事項の反映もかねて書き直しました。以前の記事にコメントくださった方すいません。

はじまり

MIDIファイルを読み込み、memcpy()でデータを変数にコピー。
しかしなぜか値が合わない。デバッガでメモリを覗くと順番が変…。
そういえばエンディアンがどうとか聞いたな…。

環境

  • windows11(intel core i CPUを使用)

検証コード

#include<cstdio>
#include<cstdint>


int main(void) {
	uint32_t num = 0x12345678;

	uint8_t* p_num =(uint8_t*)&num;

	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づつ区切って下位ビットから順に置かれている。
図にするとこんな感じ。てっきり上位ビットから置かれているもんだと思ってた。

image.png

原因

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;
}

まとめ

メモリを細かく制御するときはエンディアンを意識しよう

ビッグエンディアンに統一してくれないかな

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1