いつものようにmemsetを正しく使う
1860 ワード
前段のプロジェクトで問題が見つかりました.プログラムは常にdynamic_にあります.castが動的変換を行う際に異常が発生し,半日調べたところmemsetの使用に問題があることが判明し,問題自体は明らかであるが,数十万行のコードレベルにあると,それほど位置決めが容易ではない.
本文はmemsetを用いたいくつかの注意すべき点をまとめ,内容は簡単であるが,皆さんの役に立つことを願っている.
1.memsetはバイト単位でメモリブロックを初期化します.
バイト単位の配列を初期化する場合、memsetで各配列単位を任意の値に初期化できます.たとえば、
他のベースタイプを初期化する場合は、たとえば、
2.構造体タイプにポインタが含まれている場合、memsetを使用して初期化するときは注意が必要です.
例えば次のコードでは、
memsetが初期化されるとp_は初期化されませんxが指すint配列ユニットの値は、メモリが割り当てられたp_をxポインタ自体が0に設定され、メモリが漏洩します.同様にstd::vectorなどのデータ型に対してもmemsetを用いて初期化すべきではないことが明らかになった.
3.構造体またはクラス自体またはそのベースクラスに虚関数が存在する場合もmemsetを慎重に使用する必要があります.
この問題は冒頭の項目で発見された問題で、以下のコードで、
プログラムはdynamic_に実行されますcast時に異常が発生しました.理由は、データ構造MyParametersのdataやbufを初期化することを目的としています.通常、初期化が必要なメモリ空間はsizeof(int)*3*2=24バイトですが、memsetを使用してMyParametersタイプのデータ構造を直接初期化する場合、sizeof(my_pars)は28バイトです.マルチステートメカニズムを実現するため、C++虚関数のあるオブジェクトには虚関数テーブル(V-Table)を指すポインタが含まれ、memsetを使用するとその虚関数テーブルのポインタも0に初期化され、dynamic_castもRTTI技術を使用しており、実行時にV-Tableを使用するが、この場合V-Tableとのリンクが破壊されているため、プログラムに異常が発生する.
本文はmemsetを用いたいくつかの注意すべき点をまとめ,内容は簡単であるが,皆さんの役に立つことを願っている.
1.memsetはバイト単位でメモリブロックを初期化します.
バイト単位の配列を初期化する場合、memsetで各配列単位を任意の値に初期化できます.たとえば、
char data[10];
memset(data, 1, sizeof(data)); // right
memset(data, 0, sizeof(data)); // right
他のベースタイプを初期化する場合は、たとえば、
int data[10];
memset(data, 0, sizeof(data)); // right
memset(data, -1, sizeof(data)); // right
memset(data, 1, sizeof(data)); // wrong, data[x] would be 0x0101 instead of 1
2.構造体タイプにポインタが含まれている場合、memsetを使用して初期化するときは注意が必要です.
例えば次のコードでは、
struct Parameters {
int x;
int* p_x;
};
Parameters par;
par.p_x = new int[10];
memset(&par, 0, sizeof(par));
memsetが初期化されるとp_は初期化されませんxが指すint配列ユニットの値は、メモリが割り当てられたp_をxポインタ自体が0に設定され、メモリが漏洩します.同様にstd::vectorなどのデータ型に対してもmemsetを用いて初期化すべきではないことが明らかになった.
3.構造体またはクラス自体またはそのベースクラスに虚関数が存在する場合もmemsetを慎重に使用する必要があります.
この問題は冒頭の項目で発見された問題で、以下のコードで、
class BaseParameters
{
public:
virtual void reset() {}
};
class MyParameters : public BaseParameters
{
public:
int data[3];
int buf[3];
};
MyParameters my_pars;
memset(&my_pars, 0, sizeof(my_pars));
BaseParameters* pars = &my_pars;
//......
MyParameters* my = dynamic_cast(pars);
プログラムはdynamic_に実行されますcast時に異常が発生しました.理由は、データ構造MyParametersのdataやbufを初期化することを目的としています.通常、初期化が必要なメモリ空間はsizeof(int)*3*2=24バイトですが、memsetを使用してMyParametersタイプのデータ構造を直接初期化する場合、sizeof(my_pars)は28バイトです.マルチステートメカニズムを実現するため、C++虚関数のあるオブジェクトには虚関数テーブル(V-Table)を指すポインタが含まれ、memsetを使用するとその虚関数テーブルのポインタも0に初期化され、dynamic_castもRTTI技術を使用しており、実行時にV-Tableを使用するが、この場合V-Tableとのリンクが破壊されているため、プログラムに異常が発生する.