いぬでもわかる

iOSやCocos2dxが好きでときどきAndroid。できるだけ毎日何かしら書く事を目標に頑張る。

自動変数の寿命と戻り値の関係

cocos2d-xを触るにあたってc++を真面目に勉強しています。その中で、これまで知らなくて目から鱗が落ちた事が合ったので備忘録的に残しておきたいと思います。

c++というかcでは、ローカル変数のスコープはブロック内に閉じ、ブロックを抜けた後の変数の存在は保証されません。このような、1時的な変数を自動変数と呼びます。ブロック内でスコープが閉じるっていうのは感覚的に知っているっていう事は多いと思います。

void Hoge(int b){
  int c;
  for(int d = 0; d < 9; ++){
    int e;
    //eの寿命はこのブロックが終わるまで
  }
  //dの寿命はこのループが終わるまで

  //cの寿命はこのメソッドを抜けるまで
}


しかし自動変数の寿命は、ブロック内と言っているのにも関わらず戻り値として自動変数を返しそれを利用する事ができます。これってどういう事なんでしょう。

int intValue(){
    int value = 10;
    return value;
}

printf("value %d", intValue());

まず、本当に自動変数の寿命が関数内で閉じているのか確認みます。関数内で定義された自動変数のポインタを返す関数を定義してみました。これは、ワーニングは確認できますが、エラーになりません。しかし、実行してみると実行時エラーになります。これは、一番最初に言った通り自動変数の寿命はブロック内なため、関数が終了した段階でvalueの存在が保証されないため、不正なアドレスにアクセスしてしまいエラーになっています。ここから、戻り値としては自動変数ではない何かが返ってきているらしい事がわかりました。

int* intPointer(){
    int value = 10;
    printf("tes address: %p", &value);
    return &value;
}

printf("return pointer address: %p", intPointer());


では、何が返ってきているのでしょうか。試しに、intValue関数内の変数と戻り値として帰ってくる変数とが同じ実体なのか確認してみるために、アドレスの確認を行ってみます。しかしこのコードは実行できません戻り値のアドレスを表示しようとしている部分がエラーになります。エラー内容はコンパイラによって異なりますが、Xcodeでは、"Cannot take the addresss of an rvalue of type 'int'"と表示されます。日本語に訳すと、int型のrvalueのアドレスは取得できませんといった感じです。rvalueというのは、一時作られるオブジェクトの事で参照されなくなった時点で破棄されます。rvalueのアドレスは取得できません。元の話に戻ると、上記から関数の戻り値で返ってくる値は、関数内で使われている自動変数ではない異なるオブジェクトだということがわかります。

int intValue(){
    int value = 10;
    printf("tes address: %p", &value);
    return value;
}

printf("value Pointer %p", &(intValue()));


この一時的なオブジェクトは、意識することなく利用されていると思います。僕が感動したのが後置インクリメントです。後置インクリメントの戻り値は、括弧で囲って優先度を高めようが高めまいがインクリメント前の値が返ってきていますよね。これ実は、後置インクリメントの戻り値にはインクリメント前の値をコピーした一時的なオブジェクトが返ってきているのです。後置インクリメントは、前置インクリメントに比べて速度が遅いと言われる理由もここにあります。c++プログラマは知っていて当然の知識なのかもしれませんがコレ本当に目から鱗がおちました...

int i = 0;
int result = 10 + (i++);
printf("%d", result);