Consider the following C++ function:
void doStuff() {
Thing thingA;
Thing thingB;
thingA.doSomething();
// .. etc
}
During the execution of this function, variables thingA
and thingB
are allocated on the stack. That means that the variable thingA
represents some address on the stack, and it’s value is what’s allocated in this address (correct me if I’m wrong).
What I don’t understand is this: when we call thingA.doSomething()
, how does the CPU know what the address represented by thingA
is? The value held by thingA
is currently buried in the stack under some other data. How does the CPU know what the address that leads to this value is, in order to reach this data?
3
The compiler will choose a different offset from the stack pointer for each stack-allocated variable. For example, stackPointer + 0 bytes
might point to thingA
, and stackPointer + 8 bytes
might point to thingB
.
The compiler will make sure that the offsets are large enough that the memory used for each variable doesn’t overlap. On each function call, the stack pointer is moved to make space for the callee’s variables. On function return, the stack pointer is reset back to where it was in the caller, so that variable references in the caller still work correctly.
2
The first thing to note is that to the CPU, the stack is just a portion of the total memory that gets used in a particular way. Addresses of objects on the stack are indistinguishable from addresses of other objects (globals and heap allocated objects).
When invoking a method or a function, the compiler knows where the object and parameters are located and tells the compiler where to look for them. This can be a memory address or a CPU register.
Due to the way that the stack is typically used, objects on the stack usually don’t have a fixed address. But they do have a fixed address relative to the other stack-allocated objects in the same function and that knowledge is used to determine where they are exactly located.
In most CPU architectures, two registers are used to keep track of the top of the stack and the location relative to which the stack-allocated objects in the current function invocation are located. These registers are sometimes referred to as respectively the stack pointer and the base pointer.