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 base

The 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), %eax

In this example, the compiler uses stack-frame locations for values associated with the C names:

C NameExample Stack LocationRole 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 code

But 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 = 5

The 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.