memset関数の意味

Cの標準ライブラリ関数にmemsetというのがある。

というのを最近まで知らなかったんだけど。

移植作業をしているプログラムではわりと見るので知ったんだけど。


memset((void*)&foo, 0x00, sizeof(foo));

構造体を全部0x00で埋めて初期化するというのでこんな書き方がある。

こういう書き方をすることは知らなかったけど、なるほどなと。


配列を0埋めするという使い方が多いのかと思ったのだが、

構造体に対して適用されている例が多い印象はあった。

ちょっと変な使い方としてはこんなものあって……

memset((void*)&foo.reserved2, 0xFF, 6);

構造体のメンバーreserved2を含む6byteを0xFFで埋めるという内容。

6byteを埋めるならmemsetを呼び出す理由もわからなくはないが、

ところによっては1byteとか2byte埋めるだけにmemsetが使われているところも。

それはもはや foo.reserved3=0xFFFF; と書いた方がよいのでは?

他のmemsetと表記を合わせたかったのだろうか。


memset関数というのは内部的には2byte, 4byteアクセスなどに変換しているとみられる。

シンプルに言えばmemsetというのはこういう処理ではある。

unsigned char* p=s;
for(size_t i=0; i<n; i++){ p[i]=c; }

ただ、1byteずつポロポロと書き込みを行うのは効率が悪いので、

上記でpが4バイトアライメントされていて、かつnが4の倍数とすれば、

unsigned int cx4 = (c<<24)|(c<<16)|(c<<8)|c;
for(size_t i=0; i<n; i+=4){ *((unsigned int*)(p+i))=cx4; }

のような効率化が可能ということになる。

そういうのを内部的に判断してやっているという点で価値があるのかもしれない。


実は移植作業の中でmemsetは数を減らしていく方向ではある。

というのもバイトオーダーの差などで従来通り適用できないケースがあるため、

少量であればバラして表記する方が確実性が高いという判断である。

初期化サイズが大きいところでは従来通り使うとは思うが、

サイズが小さい場合はあえてmemsetを使うメリットも少なくて、

冒頭に書いた例にしてもfooの構造体メンバーが3つならば、

foo.a=0; foo.b=0; foo.c=0;

で特に問題ないという理屈である。

パディング部分も含めて初期化することを狙っているケースもあるが、

そういうのもよく検討すれば問題にならないのではないか。