Local variables may be represented by stack locations, registers, or no separate runtime location after optimization.
Learning Question
When assembly shows an address such as -20(%rbp), is that the C variable?
In C, local variables are written as names:
int result = left + right;In assembly, the same function may show locations like:
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
movl %eax, -4(%rbp)This raises a common confusion:
Did the C variables become these stack offsets?
The careful answer is:
In this generated code, those offsets are stack-frame locations used to store values associated with C parameters and local variables. They are not the source-level variables themselves.
What the Stack Is For
The stack is a region of process memory used for function execution.
At a programmer-facing level, the stack is commonly used for:
- function call information
- return addresses
- saved register values
- local storage for a function
- temporary compiler-generated storage
This chapter focuses on local storage.
When a function needs memory associated with one call of that function, the compiler may use the stack.
That memory belongs to the current function call while that call is active.
When the function returns, that function call’s stack storage is no longer used as that call’s local storage.
What a Stack Frame Is
A stack frame is the portion of the stack associated with one active function call.
It is not a C language object.
It is a machine-level organization used by generated code and calling conventions.
At a simplified level, a stack frame may contain:
- saved information needed to return to the caller
- saved register values
- local variables placed in memory
- temporary storage used by generated instructions
The exact layout depends on the platform, compiler, optimization level, and function.
For learning, the important idea is:
A stack frame gives one function call a concrete area of memory where generated code can store values.
Why Offsets Such as -20(%rbp) Appear
In x86-64 assembly, memory can be addressed relative to a register.
In examples compiled with GCC -O0, the register %rbp is often used as a stable frame base.
An expression such as:
-20(%rbp)means a memory location at an offset from the address held in %rbp.
You can read it conceptually as:
the memory location 20 bytes before the frame baseThe exact address is not written directly in the instruction.
The instruction says how to calculate the address from %rbp and the offset.
This is why stack locations often appear as offsets rather than as variable names.
Example: Parameters and a Local Variable
Consider the C function:
int add(int left, int right)
{
int result = left + right;
return result;
}With GCC -O0 on x86-64 Linux, one possible assembly sequence includes:
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
movl -20(%rbp), %edx
movl -24(%rbp), %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eaxIn this example, the compiler uses stack-frame locations for values associated with the C names:
| C Name | Example Stack Location | Role in This Generated Code |
|---|---|---|
left | -20(%rbp) | stores the first parameter value |
right | -24(%rbp) | stores the second parameter value |
result | -4(%rbp) | stores the computed local result |
This table describes one generated example, not a universal rule.
It is useful because it shows how C names can be represented by concrete storage locations.
Why Parameters May Be Stored on the Stack
In the example, the first two instructions are:
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)On this platform, the first two integer arguments arrive in registers.
At -O0, GCC stores them into stack-frame locations.
This makes later code read them from locations that are stable and easy to associate with source-level parameters.
That does not mean parameters always live on the stack.
With optimization enabled, the compiler may keep parameter values in registers and never create visible stack slots for them.
The stack slots appear here because this build is intentionally easy to inspect.
Why a Local Variable May Be Stored on the Stack
The computed result is stored here:
movl %eax, -4(%rbp)The source-level local variable is:
int result = left + right;At -O0, the compiler often gives a local variable a stack slot so the generated code stays close to the source structure.
That makes the relationship easy to see:
source-level result -> value stored at -4(%rbp) in this generated codeBut the stack slot is not the C variable in a language-level sense.
It is the storage location chosen by this compiler output to hold the value associated with that local variable.
Local Does Not Always Mean Stack
It is common to hear that “local variables are on the stack.”
That statement is useful as a first approximation, but it is not always correct.
A local variable may be:
- stored in a stack frame
- held entirely in a register
- split across different locations at different moments
- removed as a separate runtime location by optimization
- represented only indirectly through the behavior of other instructions
The C language defines the variable’s scope, type, and lifetime rules.
The compiler chooses a representation that preserves the required behavior.
For simple unoptimized examples, stack slots are visible.
For optimized examples, the source-level variable may not have a stable memory address unless the program requires one.
Lifetime and Storage Are Different Questions
A local variable has a C-level lifetime.
For an automatic local variable inside a function, that lifetime is tied to execution of the block where it is declared.
A stack slot is a lower-level storage location.
The two are related when the compiler uses the stack slot to represent the variable’s value.
But they are not identical.
The lifetime question asks:
During which part of the C program is this object valid?
The storage question asks:
Where is the value represented at this point in the generated instructions?
Assembly helps with the second question.
C language rules define the first question.
Stack Addresses Are Runtime Addresses
The expression -20(%rbp) is not a fixed address that is the same every time the program runs.
It is an address calculation relative to the current frame base.
When the function is called, %rbp has a runtime value.
The effective address is computed from that runtime value plus the offset.
This matters because separate calls to the same function can have separate stack frames.
The same source variable name can therefore correspond to a similar offset pattern in different function calls, but the actual memory addresses belong to each active call.
This will matter more in the next chapter on function calls.
Why Stack Frames Help Debugging
At -O0, stack-frame locations often make debugging easier.
A debugger can connect source-level local variables to stack locations and show their values while the function is active.
That debugging view is useful, but it can hide an important boundary.
The debugger may show:
result = 5The generated instructions may show:
movl %eax, -4(%rbp)Both views can be true at different levels.
The debugger reconstructs a source-level view.
Assembly shows the lower-level storage and movement used in the compiled program.
What This Chapter Does Not Explain Yet
This chapter explains local variables and stack-frame locations.
It does not yet fully explain:
- the complete function call sequence
- return addresses
- caller and callee responsibilities
- all calling-convention rules
- why stack frames may be omitted in optimized builds
- heap allocation
- global variables
- virtual memory
Those topics require the stack-frame idea, but they should not be mixed into the first explanation of stack storage.
For now, the key boundary is:
A stack location is one possible machine-level storage representation for a value associated with a C parameter or local variable.
Core Mental Model
Keep these boundaries separate:
- A C local variable is a source-level name with C language rules.
- A stack frame is a region of stack memory associated with one active function call.
- A stack offset such as
-20(%rbp)identifies a memory location relative to a frame base. - In unoptimized code, parameters and locals are often stored in stack slots to make source-level structure visible.
- In optimized code, the same source-level variables may be held in registers or optimized away as separate locations.
When reading assembly, ask:
Is this instruction reading or writing a stack-frame location, and which source-level value is that location being used to represent in this generated code?
Final Summary
Local variables do not literally become C names inside the running machine.
In one common unoptimized representation, the compiler stores values associated with parameters and local variables in stack-frame locations such as -20(%rbp).
Assembly makes those storage choices visible, while C explains the source-level meaning those choices must preserve.