Page List

Search on the blog

2010年7月24日土曜日

memset()とmemcpy()

恥ずかしながら、Cのmemset()とmemcpy()を今まで使ったことがなかった。
memset()は欠点があるみたいな話を聞いたことがあったので、「あーじゃ要らない機能なんだー。。」
くらいに思っていた。

今回、TopCoder editorialに投稿された解答がmemset()、memcpy()を使っていたので改めて勉強しなおした。
これは、なかなか使えそう。。。

まずは、memset()について見てみよう。
ここで問題!あなたは普段、配列を初期化する時どうしていますか?

ちなみに、宣言時にすべての要素を0に初期化する場合は、これでOKです。(disp()は確認用に標準出力へ出力するためのコードです。以下同様。)



#define forf(i, n) for(int i=0; i<(int)(n); i++)
#define disp(x) cout << #x << " : " << x << endl

using namespace std;

int main () {
int x[10] = {0};

forf(i, 10)
disp(x[i]);
return 0;
}


これは、まだ楽ですね。

じゃあ、宣言した後で、初期化するときは?


int main () {
int x[10];

forf(i, 10)
x[i] = 0;

forf(i, 10)
disp(x[i]);
return 0;
}


上記のようにやってる人。。。ちょっと面倒臭くないですか?
これを解決してくれるのがmemset()です。


int main () {
int x[10];

memset(x, 0, sizeof(x));

forf(i, 10)
disp(x[i]);
return 0;
}


さてさて、では0ではなく1に初期化するときは?


int main () {
int x[10];

memset(x, 1, sizeof(x));

forf(i, 10)
disp(x[i]);
return 0;
}

と思いきや、これを実行すると、16843009に初期化されてしまいます。ちょっと考えてみて気付きました。

16843009 = 1 + 2^{8} + 2^{16} + 3^{24}

なるほど、配列の要素を初期化するのではなく、メモリを1byte単位で初期化してるのね。。。うちのマシーンは、intが32bit(= 4 byte)なので、0x01010101に初期化されていたということです。
なるほど。。。

つなりこのmemset()という関数は、変数のサイズが1byteであるcharに使うってのが最も基本的な関数なようですね。。そうか!この要件を担保するためにcharのサイズは環境依存なしの1byteなのか!!(個人的な予想。。)

っと、ちょっと話が脱線しましたかね。
ではmemset()のさらに便利な使い方を。。2次元配列の初期化2重ループでやってませんか?そうです。これでイケちゃいます!!




int main () {
int x[10][10];

memset(x, 0, sizeof(x));

forf(i, 10)
forf(j, 10)
disp(x[i][j]);
return 0;
}

これでmemset()の便利さは分かりましたね!
さて、次はmemcpy()。細かい事はここまで読んでくれた人なら、多分分かってくれるはず。
memcpy()を使えば、以下のようにして2次元配列のコピーが可能です!


int main () {
int x[3][3];
int y[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};

memcpy(x, y, sizeof(y));
forf(i, 3)
forf(j, 3)
disp(x[i][j]);
return 0;
}



ちなみにmemcpy()の第3引数では、コピー元から何バイト分だけコピー先へコピーするかを指定します。
さー、ここまで来て、memset()、memcpy()はどうやらかなり出来るヤツらしいということが判明しました。では、これらの関数の欠点って何??
ちょっとネットで調べましたが、よさそうなページは見つけられませんでした。

自分なりに考えてみましたが、この問題点は、先に説明した1で初期化するつもりが0x01010101で初期化されるってことに関係してる気がします。
私が言いたいのは、intのサイズが32bitのマシンで意図的に配列の要素が0x01010101に初期化されるようなコードを書いた時に、これを16bitのマシンに移植すると、0x0101に初期化されることになる。
これが問題なのではないかな~~。と。。
自分ひとりで開発するなら問題ないけど、複数チームで複数環境で開発なんてやってるときに、この辺りを理解せずにむやみやたらにmemset()を使ってたら危険だぞ!ってことですね。

他に何か欠点をご存知の方いらっしゃいましたら、是非是非コメント頂ければ。。


0 件のコメント:

コメントを投稿