【C言語】【スタック】ローカル変数を戻り値にしても問題ない? --------------------- int test(void) { int b = 0; b = 1 + 1; return b; }

C言語関連 | プログラミング92閲覧xmlns="http://www.w3.org/2000/svg">25

ベストアンサー

0

ThanksImg質問者からのお礼コメント

動画まで作成していただき大変ありがとうございました。 とてもわかりやすく疑問が解決しました。

お礼日時:11/14 17:12

その他の回答(10件)

0

/*既に充分だと思いますが僕なりに書きます。*/ // 戻り値は呼び出し元に一時コピーされます(多分)。 // 静的確保のローカル変数はスタックに確保されますが、関数終了時にスタックは破棄されます。 // であれば、スタックから破棄された変数へのポインタを返されても意味が無い(使えない)ですよね。 // 対して、どこか(ヒープや呼び出し元)に実体があったり、int 等の値であれば、ちゃんと意味がある(使える)ので問題ありません。ポインタでも構いません。 int MyFunc1() { int n = 0; return n; // 呼び出し元に n の値 0 がコピーされます。問題ありませんね。 } int *MyFunc2() { int n = 0; return &n = 0; // 呼び出し元に n へのポインタ がコピーされます。 // ポインタの指す先( n の領域)はスタックにありましたが、既に破棄されたので使用できません。 } int *MyFunc3() { int *pN = (int*)malloc(sizeof(int) * 1); // 合ってないかも return pN; // 呼び出し元に pn の 値 = アドレス がコピーされます。 // ポインタはヒープに動的確保されたメモリを指すので使用できます。 // よく使う手法です。 } int *MyFunc4(int *a_pNum) { int *pN = a_pNum; return pN; // 呼び出し元に pN の値 = a_pNum へのポインタ がコピーされます。 // 呼び出し元が渡したアドレスなのでどこかに実体があるはずです。 // これに似たようなことはよくします。 } // 以下おまけ int **MyFunc5(int *a_pNum) { int *pN = a_pNum; return &pN; // これは MyFunc2() と同様に意味の無い値が戻ってきます。 } int MyFunc6(int *a_pNum) { int *pN = a_pNum; return *pN; // これは pN の指す先 = a_pNum の指す先が戻ってきます。 // 似たようなことはたまにします。 }

0

よくよく考えてみると return b; がダメなら return b+1; もダメでしょう。 そういうことはありませんね。 だから return b もOKです。 となると何故 test(void)の return b; は良いのに int *ptest(void){ int b=0; return &b; } はダメなのかが疑問になってきます。 良いと言っても、実は処理系とかプロセッサ アーキテクチャがそこそこ工夫してます。 関数を出て無効になっているbが本当に内容を 破壊されうるまでの間にすばやくその値を 退避領域に退避させるようなことをしてます。 (プロセッサレジスタも利用できる場合は 当然利用することでしょう) autoなローカル変数へのポインタを返された 場合は、例えそういった工夫をしても、 ポインタとして使えるとも限らないような ものを返す可能性があり「モラル」に反する という意味でダメということになります。 (かつては(今も?)警告だけが出てエラーに はならないこともあるでしょう)

0

>#ローカル変数がポインタの場合は戻り値にしてはいけないことはわかっています。 #ローカル変数がポインタでない場合、ローカル変数を戻り値にしてよいか知りたいです。 なるほど、ポインタの問題があるからそう考えてしまったんですね。 安心してください。 戻り値はレジスタ渡しなので、スタックは関係ありません。 考え方が逆なんだと思います。 確かに、ローカル変数は基本的にスタック領域を使うので、そのまま元の関数で使うわけにはいきません。 そこで、戻り値と言う機能があるのです。 そして、戻り値を使っても問題が発生してしまうのが、ポインタの場合です。 でもこれも問題ない場合とある場合があります。 つまり、ポインタの指す値がグローバルや、親関数のローカルの場合は問題が起きません。 子関数内のローカルを指していた場合だけが問題になるのです。 この理由はご理解してるようですね。

0

ほとんどのCコンパイラで、関数の戻り値は、CPUのレジスタを使用しているので、変数のメモリ空間がどこにあろうが無関係です。 関数 test()の場合はレジスタ(x86ならEAX)に2をコピーして ret します。

0

return による戻り値は、メモリーを介してではなく CPU内のレジスターに記憶され、このレジスターを介して 受け渡しが行なわれるため、ローカル変数は関与しないから問題無い。 レジスターの名前表記は、アセンブラー言語よっても異なる。 ここではレジスターを REG として受け渡しの流れを示すと以下の通り。 …// return b ; の部分 REG = b ;// b が解放されるのは、この直後。 return ; } …// a = test() ; の部分 test() ; a = REG ; … } return は復帰するだけの機能で、それ自体には、 戻り値を受け渡しするような機能は無いってこと。