TITLE: stack frames and exceptions (Newsgroups: comp.lang.c++.moderated, 7 Nov 98) [ This part of a discussion about stacks, memory, and exceptions. -adc ] GLASSBOROW: Francis Glassborow wrote: > Maybe I am just plain stupid but I cannot understand how you intend to > guarantee that 'way down there'. Suppose: > > int main () { > try { > Y y; > X x; > } > catch (badX bx) { } > } > > Now suppose that ~Y() requires a large stack frame, where were you > proposing it went? DEROCCO:: "Paul D. DeRocco" It goes wherever the stack pointer happens to be at the time the destructor is called. In this case, it would be "way down" past the object that was thrown. Consider this: void inner(int n) { throw string("Hi, mom!"); } struct X { ~X(); }; void another(int); void outer() { int i = 0; try { X x; inner(i); } catch (const string& m) { cout << m << endl; another(i); } } The outer() function creates a stack frame containing i, an exception context, and x, all accessed relative to the base pointer. One of the things in the exception context is the value of that base pointer. When it calls inner(), it pushes i onto the stack in its temporary area, and inner() makes that part of its base area. Its base area is now pointed to by the base pointer, which contains the previous value of the base pointer from outer(), in case it needs to return. The stack pointer is below that (assuming a downward growing stack), "protecting" everything. When the inner() function throws the string object, it creates the temporary string on the stack in its temporary area, pushing the stack pointer further down. It then looks in some global (or thread-local) variable that records the location of the most recent exception context, i.e., the one in outer(), loads the base pointer from that context, and jumps to the compiler-generated code that handles the stack unwinding for that function. The stack pointer is still below the string object and inner's stack frame; only the base pointer has moved. That code computes the address of x from the base pointer, pushes it onto the stack into its temporary area, which is still below the string object and inner's now obsolete stack frame, and calls the destructor. The destructor sets up its stack frame to contain this parameter and the previous base pointer value (i.e., outer's), destroys the object, and returns. When it returns, the stack pointer winds up back where it was, just below the temporary string object and inner's dead stack frame, and the base pointer again points to outer's stack frame. The exception handling code checks the type of the exception, finds a match with the catch clause, and executes the code in the catch clause. The catch clause's temporary area is still below the string object and inner's dead stack frame. Yet it also has access to outer's stack frame, so that it can pass i to another(), and so on. When the catch clause is finally finished, string::~string() is called, and THEN the stack pointer is cut back to the bottom of outer's stack frame, freeing the space occupied by inner's long dead stack frame and the string object. Fun stuff, huh?