私は c++ からやり直し、変数のスコープについて考えていました。関数内に変数があり、その変数を返す場合、その変数は、スコープが終了したために返されたときに「死んで」いませんか?
文字列を返す関数でこれを試してみましたが、うまくいきました。誰でもこれを説明できますか?または、少なくともこれを説明できる場所を教えてください。
ありがとう
私は c++ からやり直し、変数のスコープについて考えていました。関数内に変数があり、その変数を返す場合、その変数は、スコープが終了したために返されたときに「死んで」いませんか?
文字列を返す関数でこれを試してみましたが、うまくいきました。誰でもこれを説明できますか?または、少なくともこれを説明できる場所を教えてください。
ありがとう
関数が終了すると、次の手順が実行されます。
関数の戻り値は、この目的のためにスタックに置かれたプレースホルダーにコピーされます。
スタック フレーム ポインタの後のすべてがポップ オフされます。これにより、すべてのローカル変数と引数が破棄されます。
戻り値はスタックからポップされ、関数の値として割り当てられます。関数の値が何にも割り当てられていない場合、割り当ては行われず、値は失われます。
次に実行する命令のアドレスがスタックからポップされ、CPU はその命令で実行を再開します。
値を返すと、コピーが作成されます。ローカル変数のスコープは終了しますが、コピーが作成され、呼び出し元の関数に返されます。例:
int funcB() {
int j = 12;
return j;
}
void A() {
int i;
i = funcB();
}
j の値 (12) がコピーされて i に返されるため、i は 12 の値を受け取ります。
それは、返す変数の種類によって異なります。プリミティブを返す場合、それは参照ではなくコピーによって返されるため、値は呼び出し元の関数が取得できるスタックの一番上にコピーされます (または、より多くの場合、レジスタに配置されます)。オブジェクトまたはメモリをヒープに割り当ててポインターを返す場合、スタックではなくヒープ上にあるため、ポインターは死ぬことはありません。ただし、スタックに何かを割り当ててそれを返すと、それは悪いことです。たとえば、次のいずれかは非常に悪いものです。
int *myBadAddingFunction(int a, int b)
{
int result;
result = a + b;
return &result; // this is very bad and the result is undefined
}
char *myOtherBadFunction()
{
char myString[256];
strcpy(myString, "This is my string!");
return myString; // also allocated on the stack, also bad
}
もう少しメモリモデル指向の説明のために: 関数が呼び出されると、関数がそのローカル変数を配置するための一時的なスペースが作成され、これはframeと呼ばれます。関数 (呼び出し先) が値を返すと、それを呼び出した関数 (呼び出し元) のフレームに戻り値を配置し、呼び出し先のフレームを破棄します。
「フレームが破棄される」部分が、関数からローカル変数へのポインターまたは参照を返すことができない理由です。ポインターは実質的にメモリ ロケーションであるため、フレームが破棄されると、ローカル変数 (定義上、フレーム内の変数) のメモリ ロケーションが正しく返されなくなります。呼び出し先フレームは値を返すとすぐに破棄されるため、ローカル変数へのポインターまたは参照はすぐに正しくなくなります。
これは、返品されたアイテムの種類によって異なります。値で返す場合は、変数の新しいコピーが作成され、呼び出し元に返されます。オブジェクトの有効期間について心配する必要はありませんが、オブジェクトをコピーするコストについて心配する必要があるかもしれません (ただし、時期尚早に最適化しないでください - 正確性がはるかに重要です)。
std::string someFunc( std::string& const s)
{
return s + "copy";
}
関数が参照を返す場合、その有効期間は関数の有効期間を超えて延長する必要があり、オブジェクトの作成にdelete
使用している場合、呼び出し元が必ずしもそれを実行できるとは限らないため、返すものに注意する必要があります。new
std::string& someFunc2( std::string const& s)
{
return s + "reference to a copy"; // this is bad - the temp object created will
// be destroyed after the expression the
// function call is in finishes.
// Some, but not all, compilers will warn
// about this.
}
もちろん、ポインターを返す場合も同様の有効期間の考慮事項があります。
ローカル変数は戻り値にコピーされます。コピー コンストラクターは、重要なクラスに対して呼び出されます。
ローカル変数へのポインターまたは参照を返すと、直観が示唆したように、問題が発生します。