関数の戻り値の寿命

とある関数の戻り値としてポインタもしくは参照を返す時、実際に戻り値として返す値にローカル変数を指定するとまずいってことは基本中の基本ですが、意外と周知ではないらしい……ということで。ポインタと参照を一緒くたに扱うのは面倒なのと混乱を来たす恐れがあるので次の例ではポインタのみを使用します。

int* hoge()
{
    int local;
    /* ...何か色々やってる... */
    return &local;
}

void nantoka_function()
{
    int* tmp = hoge();
    // hoge()の戻り値はhogeのローカル変数であり、
    // つまりこの時点でhogeのスコープから抜けているため、tmp変数の内容は保障されない!
}

保障されない! なんて書いてみたものの、上の例だと自分の環境(VC7)では普通に動作してしまいました。ここで華麗にassertで落っことす予定だったのですが、値の参照だけなら平気だってことでしょうか。しかしそれでは面白くないので、これなら絶対落ちるだろうという例も作ってみました。しかし、たまたま動いたとはいえ上の例が宜しくないことには違いないので、上みたいなコードは書かないように。

std::vector* hoge()
{
    std::vector local;
    /* ...localに色々やってる... */
    
    return &local;
}

void nantoka_function()
{
    std::vector* arr = func2();
    
    arr->push_back(4); //< ここで落ちる!
}

変数arr*の指し示す先にあるはずのstd::vector<>のインスタンスは、関数hogeを終了した時点で開放されてしまっているので、それに対する操作は全て失敗する、ということ。戻り値を参照にした時も全く同じ理由で落ちますので、パフォーマンス向上のために戻り値を参照にしようだなんて時についローカル変数を返したりしないよう注意しましょう。

こんなん、VCなら警告を出してくれるんで少し気をつけるだけでいいんですが、BCCだと素通りしてしまうので、意外と気付きにくかったりして厄介です。

プログラミング言語C++ (アスキーアジソンウェスレイシリーズ―Ascii Addison Wesley programming series)

プログラミング言語C++ (アスキーアジソンウェスレイシリーズ―Ascii Addison Wesley programming series)

今回は、こういうネタで嘘を書いたらやばいだろうということで上記の本を紐解き参照しつつ書いたので、間違いはないはずですが、おかしなところがあったら(過去のものも含めて)ぜひ突っ込みをお願いします。