프로그램이 실행되면 메인 메모리에서 코드 세그먼트, 데이터 세그먼트, 스택, 빈 공간, 힙 메모리로 로드된다. 함수를 실행하면 스택에서 작동이 이루어진다. 스택은 LIFO(Last In First Out) 구조로 나중에 집어넣어진 것이 제일 먼저 빼내어지는 구조다. 이 구조가 필요한 이유는 함수 실행시 호출 순서에 따라 먼저 실행된 함수가 대기상태에 있고 나중에 실행된 함수가 먼저 실행되어야 하는 C 언어의 절차형 실행 방식 때문이다.
아래와 같은 소스코드가 실행된다고 생각해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
int main(void) { int a, b, sum; sum = add(a, b); return 0; } int add(int f, int s) { int total; total = f + s; return total; } |
모든 C 프로그램은 main()부터 실행하므로 실행직후의 스택 메모리 상태는 아래와 같다.

함수를 실행할때마다 스텍 메모리에 스택 프레임이 만들어진다. 매개변수는 함수에 넘겨진 변수로 위의 예제에서는 void가 왔으므로 아무런 매개변수가 없다. 지역 변수는 피호출 함수 내부의 변수로 a, b, sum에 해당한다. 리턴주소는 함수 실행후 실행할 바로 다음 주소다. 4바이트 명령으로 실행되는 체제라면 2036번지에 main(void)가 적재되고 끝나면 2040번지의 명령어를 실행한다. 리턴값은 예제에서는 0이다.
main() 함수에서는 add() 함수를 호출한다. 이미 main() 함수가 실행중이었다면 main() 함수 실행이 잠시 멈추고 add() 함수가 스택 메모리에 적재된다. 아래와 같이 얹혀진 것으로 그려볼 수 있다.

스택은 LIFO 구조이므로 add()의 작업이 우선 실행된다. 실행이 마쳐지면 다시 main() 함수로 돌아와 남은 라인을 실행한다.
메모리 번지 체제에서는 PC(Program Counter)에 다음에 실행할 명령어 주소가 저장되어 있을때 그 주소가 1008번지라면 현재 실행 주소는 1004번지다. 1004번지 실행할때 1016번지를 점유하는 함수를 실행한다면 PC 레지스터 값을 1016으로 바꿔야 하는데, 이 경우 그전에 스택 프레임의 리턴 주소란에 1008번지를 미리 저장해두어야 복귀후 원래 하던 일을 이어서 할 수 있다.
스택 프레임의 중요한 기능은 변수의 유효 영역의 제한이다. 보통 실행 스코프라는 말을 하는데, add()가 실행중이라면 f와 s, total에만 접근할 수 있고, main()의 a, b, sum에는 불가능하다. 어느 순서의 스택 프레임인지에 따라 사용 가능한 변수가 제한된다. 그래서 다음과 같은 구조에서 main()과 add()의 변수들은 실행되는 스코프가 다르다.
add()가 실행을 마침과 동시에 팝 되어 다시 main()이 실행된다.
스택은 함수 실행마다 늘었다 줄었다 하면서 메모리 사용을 효율적으로 관리한다. 만약 무한루프가 걸리면 스택 프레임이 무한정 쌓여서 힙 메모리 영역과 맞닿게 되어 가용 메모리가 없게 되기도 한다. 이를 스택 오버플로우라고 한다.
이 글은 주우석 선생님의 저서를 보고 요약식으로 정리하였습니다.