C++ stackoverflow on main function execution

  • A+
Category:Languages

I have below code & it report stack overflow when I run it. I use pass by value to showTest(); What I expect, it will make of copy of Test struct to stack (push to stack), then at the end of function call, the Test struct will be release(pop out from stack). So I make a call for 3 times. It suppose to Push into stack & pop out at the end of each function call.

I don't see any stack issue if it push in & pop out to stack each time function called. However when I run this code, it report stack overflow on first line of main function.(I use Visual Studio 2017)

If I remove 1 of showTest() function call, then I can get it work.

Any feedback would be highly appreciate.

#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; } 

 


What evidently happens here is deferred stack popping.

In C and C++ the common calling convention is that the caller pops arguments from the stack. As a common optimisation, many compilers don't pop arguments after each call, but do that after a number of calls and pop all the accumulated arguments together. This saves a few instructions, at the expense of a larger stack which may overflow.

In MSVC, when optimisations are disabled, the compiler allocates and checks the stack beforehand for all the calls it will need in a given function. This is why the program crashes even before it prints anything.

See corresponding assembly

Some of the first instructions in main are

    mov      eax, 1200120       ; 00124ff8H     call     __chkstk     sub      rsp, rax 

This number is exactly what is needed to fit three instances of the object on the stack.

When optimisations are enabled, the compiler is smart enough to reuse the stack, so there's no crash.

Optimised assembly

    mov      eax, 400032          ; 00061aa0H     call     __chkstk     sub      rsp, rax 

Enough for a single instance.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: