Code Fix

上級

ローカル変数への参照を返すとSegmentation faultになる原因

関数のローカル変数への参照を関数の外に返してしまうバグです。関数を抜けた時点でローカル変数の実体は破棄されるため、返された参照は存在しないメモリを指すことになります。

エラーメッセージの読み方

Segmentation fault
(エラーメッセージなし)
これはプログラム自身の出力ではありません。プログラムは何も言わずに落ちています
Segmentation fault
シェルが検知した異常終了の種類 — OSが「不正なメモリアクセス」などを検出しプロセスを強制終了させました
見つけ方
このメッセージだけでは行番号すら分かりません。原因はコードを読んで特定するしかありません

このエラーが出る典型パターン

パターン1

 1  #include <iostream>
 2  int& getRef() {
 3      int        local = 42;
             ^
 4      return local;
 5  }
 6  int noise() {
 7      int junk[8] = {1,2,3,4,5,6,7,8};
 8      return junk[0];
 9  }
10  int main() {
11      int& r = getRef();
12      noise();
13      std::cout << r << std::endl;
14      return 0;
15  }
Segmentation fault

getRef()が返した参照は、関数を抜けた時点で破棄されたローカル変数localを指したままです。直後に別の関数noise()を呼ぶと、そのスタックフレームが同じメモリ領域を上書きするため、rを読んだ瞬間にクラッシュします。localをstaticにすればプログラム終了まで実体が残り続けるため、参照を安全に返せます。

直し方: intstatic int にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <iostream>
 2  int& getValue() {
 3      int        temp = 99;
             ^
 4      return temp;
 5  }
 6  int fill() {
 7      int buffer[8] = {9,9,9,9,9,9,9,9};
 8      return buffer[0];
 9  }
10  int main() {
11      int& v = getValue();
12      fill();
13      std::cout << v << std::endl;
14      return 0;
15  }
Segmentation fault

g++はこの種のミスに対して-Wreturn-local-addrという警告を出しますが、あくまで警告でありコンパイルは止まりません。static を付けてtempの寿命をプログラム終了まで延ばすのが直し方です。

直し方: intstatic int にします。

この問題を解いてみる →

パターン3

 1  #include <iostream>
 2  int& makeId() {
 3      int        id = 7;
             ^
 4      return id;
 5  }
 6  int overwrite() {
 7      int stack_data[8] = {0,0,0,0,0,0,0,0};
 8      return stack_data[0];
 9  }
10  int main() {
11      int& id = makeId();
12      overwrite();
13      std::cout << id << std::endl;
14      return 0;
15  }
Segmentation fault

「関数を抜けてもすぐ上書きされなければ値は読める」という考えは危険です。別の関数呼び出しが挟まるだけで、同じスタック領域が再利用され壊れます。ローカル変数をstaticにすれば関数を抜けても実体が破棄されません。

直し方: intstatic int にします。

この問題を解いてみる →

よくある誤解

「関数を抜けてもすぐ上書きされなければ値は読める」という考えは危険です。関数を抜けた直後に別の関数を呼ぶと、その関数のスタックフレームが同じメモリ領域を上書きするため、参照した瞬間にクラッシュすることがあります。g++はこの種のミスに対して -Wreturn-local-addr という警告を出してくれますが、あくまで警告でありコンパイルは止まりません。

まとめ

ローカル変数への参照を返すとSegmentation faultになる原因は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)