はじめに
少し考えれば当たり前かもしれませんが、以下のコードはどのような挙動をするでしょうか?
struct A {
A() { std::cerr << "constructor" << std::endl; }
std::array<char,99999999> buf;
};
int main() {
std::cerr << "hoge" << std::endl;
{
A a;
}
std::cerr << "fuga" << std::endl;
}
g++ sample.cpp -O0
一見問題ないコードに見えます。実際、c++の文法的には問題なかったはずです。が、std::array
で確保している領域が莫大なので、実行時はスタックを使い果たして何かしらのエラーが起きるなりして終了するでしょう。(最適化によってA a
の存在が消滅させられる可能性はあります)
問題は、どのような出力が得られるかです。エラーになるのはスタックに積まれるときなので、コンストラクタは実行されないはずです。ということはhoge
が出力されるのでしょうか?
しかし結果は、何も出力されませんでした。
私の誤解
お恥ずかしい限りなのですが、c++においてローカル変数の領域が確保されるのはスコープに入った時だと思っていました。これが正しければ、hoge
が出力されます。
この結果に納得いかなかった私は、なんとかしてhoge
を出力しようと試みました。
試行錯誤した結果
無理でした。というかそのような挙動になる理由がないです。上記の挙動は私が「領域の確保」を勘違いしていただけと思われます。
冷静に考えたらメモリ領域のサイズが分かっているのに複数回に分けてスタックに積む必要がありません。
また、アセンブリを覗いて気が付いたのですが、通常はスタック周りの保護のために関数呼び出し前後でコードが挿入されています。なので無茶なスタックを確保するコードを書いたら、main
関数に入る前に落ちている可能性が高いです。
再現するには関数を使ってスタックを積みなおす必要ばあります。
void func() {
std::cerr << "called" << std::endl;
A a;
}
int main() {
std::cout << "hoge" << std::endl;
func();
}
ちなみにこの場合、hoge
が出力されてcalled
は出力されません。実行時エラーのデバッグ時に、このように関数呼び出しで落ちていた場合はスタック周りを疑ってみるといいかもしれません。
正しい挙動は?
おそらくスタック周りの動作は実装依存だと思われます。(詳しい方がいたら教えてください)
コンストラクタは記述した通りに呼ばれています。
固定長だからとむやみにstd::array
を使うのは考えものですね。