局所変数、特に集成体を宣言した後、実際に必要かどうかに関わらず、必ずmemset関数でゼロクリアする人は大勢います。しかし、そんなコードを見かけたら、それを書いた人のコードはすべて疑ってかかった方がよいかもしれません。
まずは、次のコードをご覧ください。
|
0 1 2 3 4 5 6 7 8 9 |
struct A { int a; double b; char *c; }; A a[10]; memset(a, 0, sizeof(a)); |
よく見かけるコードですが、上のコードは、必ずしも期待した結果になるとは限りません。なぜなら、double型やポインタ型は、これらを構成する全ビットが0になったとしても、オブジェクトの値が0になるかどうかは分からないからです。
確かに、ほとんどの処理系では上記のコードでも問題なく、そして期待通りに動作します。しかし、それはあくまでも”たまたま”動いているに過ぎません。そうした不安定な要素をなくすために行った初期化が、かえってコードを怪しくしてしまっているのです。
C言語で、単に集成体の全要素をゼロクリアしたいだけであれば、
|
0 1 2 |
A a[10] = { 0 }; |
とすれば十分です。このように書くと、おそらく次のような反論が返ってくることでしょう。「その方法では、構造体の詰め物がゼロクリアされない」と。しかし、構造体の詰め物にアクセスして、言語仕様上保証される結果を期待することには無理があります。
構造体の詰め物をゼロクリアしたい理由は、多くの場合、memcmp関数を使って一致判定を行いたいことが理由でしょう。しかし、整数型以外をゼロクリアしても結果が保証されないのと同様、比較の場合もうまくいくとは限りません。具体的には、内部表現が異なる場合でも、値としては同じになるかもしれないからです。
あるいは、こんな反論も聞こえてきそうです。「memset関数を使った方が効率がよい」と。本当に効率がよいかどうかは実測してみるか、コンパイル結果を見て、ステップ数を計算してみてください。必ずしもmemset関数の方が効率がよいわけではないことに気付くはずです。
仮にmemset関数の方がずっと効率がよかったとしても、こんな汚い方法による最適化は最終手段にすべきです。それに、こんな初期化はそれ自体が不要な場合もあり、単なる”おまじない”に過ぎないことも多いのです。
ところで、先ほどはC言語の例でしたが、C++であれば、
|
0 1 2 |
A a[10] = {}; |
のように波括弧の中に0をひとつも書かなくてもかまいませんし、C++11以降であれば、
|
0 1 2 |
A a[10]{}; |
のように書くこともできます。

![[迷信] 今どきint型が16ビットの処理系なんて無い](https://www.kijineko.co.jp/wp-content/uploads/2021/05/4618601_s.jpg)
![[迷信] FILE型は構造体](https://www.kijineko.co.jp/wp-content/uploads/2021/06/4285286_s.jpg)
![[迷信] getsは単純にfgetsに置き換えられる](https://www.kijineko.co.jp/wp-content/uploads/2021/06/5063693_s.jpg)
![[迷信] freeにNULLを渡すとクラッシュする](https://www.kijineko.co.jp/wp-content/uploads/2021/06/4905202_s.jpg)
![[迷信] コンストラクタで自身をゼロクリア](https://www.kijineko.co.jp/wp-content/uploads/2021/06/4450701_s.jpg)
![[迷信] double の出力書式は"%lf"](https://www.kijineko.co.jp/wp-content/uploads/2021/05/4950885_s.jpg)