web-dev-qa-db-ja.com

メイン関数実行時のC ++スタックオーバーフロー例外

以下にリストされているコードがあり、実行するとスタックオーバーフローが報告されます。 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

単一のインスタンスには十分です。

26