栈上数组内存分配:分析两个C函数编译的x86-64汇编逻辑
Let's walk through exactly how GCC lays out the stack for these two functions. Since we're compiling with -O0 (no optimizations), the compiler generates straightforward, debug-friendly code that maps directly to your C source—no tricky reordering or optimizations to obscure what's happening.
Context First
We're working with:
- GCC 7.3 x86-64
- Ubuntu 16.04.1 x86-64
- Compilation flags:
-O0 -g(no optimizations, debug symbols enabled)
x86-64 uses a stack that grows downward (toward lower memory addresses), and -O0 ensures the compiler allocates stack space for every local variable exactly as declared, with minimal tweaks beyond alignment requirements.
Breakdown of func1
Your C code for func1 declares a single 2D array a[10][5] (50 bytes total, since each element is an unsigned char) plus the function parameter x.
Here's the assembly annotated to explain each step:
func1(unsigned char): pushq %rbp ; Save the old base pointer to the stack movq %rsp, %rbp ; Set the new base pointer (rbp = current stack top) movl %edi, %eax ; Copy the input parameter x (passed in %edi) to %eax movb %al, -68(%rbp) ; Store the 1-byte x in the stack frame at offset -68 from rbp movb $1, -64(%rbp) ; Set a[0][0] = 1 — this is the first byte of array a movb $2, -15(%rbp) ; Set a[9][4] = 2 — this is the last byte of array a nop ; Padding to align the stack frame to 16 bytes (x86-64 requirement) popq %rbp ; Restore the old base pointer ret ; Return to the caller
Stack Layout for func1
- The base pointer
%rbpis our reference point for all stack variables. -68(%rbp): Stores the function parameterx(1 byte, with 3 bytes of padding to maintain alignment).-64(%rbp)to-15(%rbp): The 50-byte arraya(since 64 - 15 = 49, plus the starting byte gives 50 total bytes).- The total stack frame size is 64 bytes, which aligns to the 16-byte x86-64 stack requirement.
Breakdown of func2
func2 adds a 10-byte array b[10] to the mix. Here's how this changes the stack layout, plus the full assembly we'd expect:
func2(unsigned char): pushq %rbp ; Same stack frame setup as func1 movq %rsp, %rbp movl %edi, %eax movb %al, -78(%rbp) ; x is now at -78 (10 bytes further from rbp than func1's x) movb $1, -74(%rbp) ; a[0][0] starts at -74 (shifted 10 bytes from func1) movb $2, -25(%rbp) ; a[9][4] ends at -25 (50 bytes total for a) movb $4, -14(%rbp) ; b[0] starts at -14 (10 bytes for b: -14 to -5) movb $5, -5(%rbp) ; b[9] is the last byte of array b nop ; Optional padding if needed for 16-byte alignment popq %rbp ret
Key Differences Between func1 and func2
- Larger stack frame:
func2's stack frame is 10 bytes bigger thanfunc1's, directly accounting for the extrabarray. - Shifted variable offsets: Every variable in
func2is shifted 10 bytes away from%rbp(more negative offset) compared tofunc1. This is because-O0allocates variables in declaration order: firstx, thena, thenb—sobgets placed at the lowest (most negative) address in the stack frame, pushingaandxup. - Extra initialization instructions:
func2has two additionalmovblines to setb[0]andb[9], which are missing infunc1.
Why This Behavior?
With -O0, GCC prioritizes debuggability over efficiency. It:
- Allocates stack space for every local variable in the exact order they're written in your code.
- Stores function parameters on the stack even though they're already in registers (so debuggers can easily access them).
- Adds padding to ensure the entire stack frame is aligned to 16 bytes—a mandatory requirement for x86-64 function calls.
内容的提问来源于stack exchange,提问作者toozyfuzzy

