main's frame
The program starts in main. First, main gets a frame: a block of memory on the stack with one slot for each local variable, a and b. Nothing has been stored in them yet.
int a = 3; writes 3 into a's slot. a is a local variable: it lives in main's frame, and only code inside main can use the name a.
Calling square
square(a) calls a function. The call pushes a new frame on top of the stack, with slots for square's own variables, x and y. main waits, and its frame stays underneath.
The parameter x is a local variable of square. Passing a by value copies 3 from a's slot into x's slot. They are separate slots: changing x would never change a.
int y = x * x; reads x, multiplies, and writes 9 into y. The running function only uses the slots in its own frame, on top of the stack.
Returning
return y; ends square and hands back a copy of the value in y. Back in main, that copy, 9, becomes the starting value of b.
When square returns, its frame is popped off the stack. x and y are destroyed together, automatically: no clean-up code. Their memory is free for the next call.
std::cout << b prints 9. b still exists because main has not finished: a local variable lives until the closing } of the block that declared it.
Each call pushes a frame and each return pops it: one marker, the stack pointer (SP), just moves up or down. That makes local variables very fast. But the stack is small: 1 MB by default on Windows.
At main's closing }, main returns and its frame is popped too: a and b are destroyed. The program ends with exit status 0, which tells the system it finished without errors.
What happens
;; step 1/10 · main-frame the stack: memory for the functions that are running a frame: one function call's local variables push main's frame slots for a and b a = ? b = ? no value written yet: never read them stack (top first) main a = ? b = ?
The C++ standard does not use the word stack: it says local variables have automatic storage duration, created when their block starts and destroyed when it ends. Every mainstream compiler keeps them in a call stack, though the optimiser may keep some only in CPU registers. The figure piles frames upward, like plates. In real memory the stack on x86-64 and ARM grows toward lower addresses, and a frame also holds the return address and saved registers.