[迷信] とりあえず memset で初期化

局所変数、特に集成体を宣言した後、実際に必要かどうかに関わらず、必ず memset でゼロクリアする人は大勢います。しかし、そんなコードを見かけたら、それを書いた人のコードはすべて疑ってかかった方がよいかもしれません。

まずは、次のコードをご覧ください。

struct A
{
  int a;
  double b;
  char *c;
};

A a[10];
memset(a, 0, sizeof(a));

よく見かけるコードですが、上のコードは、必ずしも期待した結果になるとは限りません。なぜなら、double 型やポインタ型は、これらを構成する全ビットが 0 になったとしても、オブジェクトの値が 0 になるかどうかは分からないからです。

確かに、ほとんどの処理系では上記のコードでも問題なく、そして期待通りに動作します。しかし、それはあくまでも"たまたま"動いているに過ぎません。そうした不安定な要素をなくすために行った初期化が、かえってコードを怪しくしてしまっているのです。

単に、集成体の全要素をゼロクリアしたいだけであれば、

A a[10] = { 0 };

とすれば十分です。こう書くと、おそらく次のような反論が返ってくることでしょう。「その方法では、構造体の詰め物がゼロクリアされない」と。しかし、構造体の詰め物にアクセスして、言語仕様上保証される結果を期待することには無理があります。

構造体の詰め物をゼロクリアしたい理由は、多くの場合、memcmp を使って一致判定を行いたいことが理由でしょう。しかし、整数型以外をゼロクリアしても結果が保証されないのと同様、比較の場合もうまくいくとは限りません。具体的には、内部表現が異なる場合でも、値としては同じになるかもしれないからです。

あるいは、こんな反論も聞こえてきそうです。「memset を使った方が効率がよい」と。本当に効率がよいかどうかは実測してみるか、コンパイル結果を見て、ステップ数を計算してみてください。必ずしも memset の方が効率がよいわけではないことに気付くはずです。

仮に memset の方がずっと効率がよかったとしても、こんな汚い方法による最適化は最終手段にすべきです。それに、こんな初期化はそれ自体が不要な場合もあり、単なる"おまじない"に過ぎないことも多いのです。

書籍紹介

参考情報