以下にリストされているコードがあり、実行するとスタックオーバーフローが報告されます。 showTest()
への値渡しを使用します。私が期待しているのは、Test
構造体のコピーをスタックに作成し(スタックにプッシュ)、関数呼び出しの最後にTest
構造体を解放(ポップ)することです。スタックから)。だから私は3回電話をかけます。スタックにプッシュし、各関数呼び出しの最後にポップオフすることになっています。
関数が呼び出されるたびにスタックをプッシュしたり、スタックからポップしたりしても、スタックの問題は発生しません。ただし、このコードを実行すると、main
の最初の行にスタックオーバーフロー例外が報告されます。 (私はVisual Studio 2017を使用しています。)
showTest()
関数呼び出しの1つを削除すると、それを機能させることができます。
フィードバックをいただければ幸いです。
#include <iostream>
struct Test
{
static int Userid;
int data[100000] = { };
Test()
{
++Userid;
};
};
int Test::Userid = 0;
void showTest(Test i_myint)
{
std::cout << "test" << std::endl;
}
int main()
{
Test *pint = new Test();
showTest(*pint);
Test *pint2 = new Test();
showTest(*pint2);
Test *pint3 = new Test();
showTest(*pint3);
return 0;
}
ここで明らかに発生するのは、遅延スタックポップです。
CおよびC++では、一般的な呼び出し規約は、呼び出し元がスタックから引数をポップすることです。一般的な最適化として、多くのコンパイラはeach呼び出しの後に引数をポップしませんが、いくつかの呼び出しの後にそれを行い、蓄積されたすべての引数を一緒にポップします。これにより、オーバーフローする可能性のあるスタックが大きくなる代わりに、いくつかの命令が節約されます。
MSVCでは、最適化が無効の場合、コンパイラはスタックを割り当てて、特定の呼び出しで必要となるすべての呼び出しを事前にチェックします。関数。これが、プログラムが何かを印刷する前にクラッシュする理由です。
main
の最初の命令のいくつかは
mov eax, 1200120 ; 00124ff8H
call __chkstk
sub rsp, rax
この数は、スタック上のオブジェクトの3つのインスタンスを収めるために必要な数です。
最適化が有効になっている場合、コンパイラはスタックを再利用できるほど賢いので、クラッシュすることはありません。
mov eax, 400032 ; 00061aa0H
call __chkstk
sub rsp, rax
単一のインスタンスには十分です。