[C] 스택 프레임

JM·2026년 9월 19일

CS

목록 보기
4/4
post-thumbnail

스택 프레임이란

 메모리 구조를 알아보면서 지역 변수와 함수 호출에 필요한 정보들이 주로 STACK영역에 저장된다는 것을 알게 되었다.

그렇다면 함수를 하나 호출할 때 스택에는 정확히 어떤 일이 일어날까?

프로그램은 함수가 호출될 때마다 함수가 실행되는 동안 필요한 정보(지역변수 등)를 저장하기 위해 스택의 일부 공간을 사용한다.
하나의 함수 호출을 위해 사용되는 스택 영역의 한 구간을 일반적으로 스택 프레임(Stack Frame)이라고 부른다.

함수의 호출

// foo.c
void foo(void)
{
	int a = 10;
    int b = 20;
}

int main(void)
{
	foo();
    return 0;
}

main()도 함수니까 스택 프레임에 올라간다. 실행 도중에 foo()를 호출하면 foo()를 위한 공간이 스택에 마련된다.

높은 주소 
┌──────────────────────┐ 
│ main함수 stack frame│ 
├──────────────────────┤ 
│ return address 	 │ 
├──────────────────────┤ 
│ foo의 지역 변수 b 	 │
│ foo의 지역 변수 a 	 │ 
│ ... 				 │ 
└──────────────────────┘ 
낮은 주소

x86-64와 같은 일반적인 환경에서는 스택이 높은 주소에서 낮은 주소 방향으로 증가하기 때문에, 새로운 함수가 호출되면 스택 포인터가 더 낮은 주소 쪽으로 이동하면서 새로운 공간을 확보한다.

스택 포인터?
메모리 스택에서 중요한 레지스터가 rsp와 rbp이다.

rsp(Stack Pointer)는 현재 스택의 Top을 가르킨다. 함수가 지역 변수를 할당할 공간을 위해 스택 공간을 더 확보하면 rsp의 값이 변경된다.

rbp(Base Pointer)는 현재 함수의 스택 프레임의 시작점을 가르킨다.

int func() {
	return 40;
}

이 코드를 disass하면 아래와 같이 나온다.

  1. 첫 번째 명령은 기존 함수의 rbp값을 스택에 보관한다.
  2. 두 번째 명령은 현재 rsp를 새로운 rbp로 설정한다.
    2-1. 새로운 rbp가 함수의 스택 프레임을 나타내는 하나의 기준점이 된다.
  3. 세 번째 명령은 rsp를 0x30만큼 낮은 주소로 이동시킨다.
    3-1. 결과적으로 현재 함수는 0x30 = 48바이트만큼의 스택 공간을 확보하게 된다.
높은 주소
		이전 함수의 stack frame 
		────────────────────── 
rbp+8 	return address 
rbp 	이전 rbp 
		────────────────────── 
rbp-8 	지역 변수 
rbp-16 	지역 변수 
rbp-24 	지역 변수 ... 
rbp-0x50 
		────────────────────── 
rsp
낮은 주소

함수의 종료

함수가 끝난다면 스택 프레임은 어떻게 될까?

함수 실행이 끝나면 해당 스택 프레임은 더 이상 사용하지 않는다.
해당 함수가 스택 프레임을 정리하고 나간다고 생각하면 된다.

일반적인 함수 종료 과정은 다음과 같다.

mov %rbp, $rsp
pop %rbp
ret

혹은 이러한 스택 프레임 복구를 한 번에 수행하는
leave 또는 ret형태의 명령이 사용될 수도 있다.

위의 사진에서는
pop %rbp 이후에 ret명령으로 스택 프레임을 정리하고 있다.

아래 사진에서는 leave와 ret명령으로 스택을 반납하는 모습이다.


이 과정에서 핵심은 함수가 종료됐다고 해서 해당 메모리 공간이 즉시 초기화되거나 물리적으로 사라지는게 아니라는 점이다.

스택 포인터와 베이스 포인터를 옮기면서 사용하는 영역이 달라질 뿐이다.

예를 들어 foo()에서 지역 배열 변수를 사용한다고 해 보자.

//foo.c
void foo(void)
{
	char buffer[64];
    
    // ...
}

foo()가 실행되는 동안 buffer은 foo()의 스택 프레임 내부에 올라온다.

foo 실행 중

┌──────────────────────┐
│ return address     │
├──────────────────────┤
│ old rbp            │
├──────────────────────┤
│ buffer[64]         │ ← 유효한 메모리
└──────────────────────┘

하지만 foo()가 반환될 때 해당 영역은 더이상 foo()의 것이 아니다. rbp와 rsp를 옮기고 공간을 반납한다.

┌──────────────────────┐ 
│ 					 │ 
│ 다시 사용할 수 있는 	 │ 
│ 스택 공간 			 │ ← 이전에는 buffer가 있던 위치 
│ 					 │ 
└──────────────────────┘

개념적으로 나타내면 위처럼 표현되겠다.

함수 반환 직후에는 메모리에 과거의 값이 잠시 남아있을 수 있다.

또 다른 함수가 호출되면 다시 rsp rbp를 옮겨 스택을 사용하기 때문에 같은 스택 공간을 사용할 수도 있다.


요약

  1. 스택 프레임은 함수가 호출됐을 때 해당 함수가 사용해야 하는 스택 영역의 공간을 말한다.
  2. 스택의 시작과 끝은 rsp와 rbp로 관리된다.
  3. 함수가 종료(반환)될 때 rsp와 rbp를 다시 되돌리며 스택 프레임을 반납한다.
  4. 반납한 메모리 공간에는 과거의 데이터가 남아 있을 수는 있다.
    4-1. 아직 덮어쓰이지 않은 것일 뿐, 그 공간을 사용해도 되는 권한이 있는 것은 아니다.
profile
개발자 지망생

0개의 댓글