msty開発メモ

技術ネタを綴ります

【C++】Zeromemoryは楽だが理解しないと危険

Zeromemoryマクロで少し面倒なことになったのでメモ。

ZeroMemoryマクロ、まあmemsetでもいいですが、指定した変数の中身を0クリアしてくれる使いやすいマクロです。

けど使い方を間違えると実行時エラーのもととなります。

以下のソースを動かしてみます。

#include<vector>
#include<iostream>
#include<Windows.h>
using std::vector;
using std::cout;
using std::endl;

struct Data1 {
	vector<int32_t> values;

	Data1(size_t size) {
		ZeroMemory(this, sizeof(Data1));
		values.reserve(size);
	}

};


int main(int32_t,char**) {
	Data1 d{5};

	for (int8_t i = 0; i < 5; i++) {
		d.values.push_back(10 * i);
		cout << d.values.back() << endl;
	}


}

Zeromemoryをよく知らない人からすれば一見問題なさそうな感じですが、
問題しかありません。
このソースを実行するとコンストラクタの「values.reserve(size);」でエラーが発生します。

Zeromemoryマクロを使ってクラスの中身全てを0クリアしているのが問題で、
vector内部で使われている変数まで0にしてしまいます。
vector内部でのみ使われているアクセス用のポインタのアドレスまで0にされているため、
正しくアクセスできず、エラーが発生します。

シンプルな回避策は以下となります

#include<vector>
#include<iostream>
#include<Windows.h>
using std::vector;
using std::cout;
using std::endl;


struct Data2 {
	vector<int32_t> *values;
	Data2(size_t size) {
		ZeroMemory(this, sizeof(Data2));
		values = new vector<int32_t>;
	}
};



int main(int32_t,char**) {
	Data2 d{5};

	for (int8_t i = 0; i < 5; i++) {
		d.values->push_back(10 * i);
		cout << d.values->back() << endl;
	}


}

この場合だとZeromemoryマクロを読んでも、Data2にあるvectorを参照するためのアドレスのみ初期化されて、その後にインスタンスを作っているので問題ありません。


あ、解放処理書いていないのでメモリリークしてますよ。

このエラー厄介なのがZeromemoryが呼ばれた場所ではなく、Zeromemoryによって初期化されたインスタンスが参照されたときに発生するので、Zeromemory書いたことを忘れてると、盲点になるんですよね。

ソースは自分。

ちなみにこれサンプルはvectorですが、mapなどのその他STLでも起きるので注意