본문으로 바로가기

스택과 힙 메모리의 이해

프로그램을 실행한다는 것은 하나 이상의 프로세스가 메모리에 로드됨을 의미한다. 프로세스가 메모리에 로드될 때 다음과 같은 구조를 가지고 로드된다. 이에 대한 자세한 내용은 이 글을 참고하자.

  • STACK: 임시 데이터(함수 호출, 로컬 변수 등)
  • HAEP: 코드에서 동적으로 만들어지는 데이터
  • BSS: 초기화되지 않은 데이터
  • DATA: 초기화된 데이터
  • TEXT(CODE): 코드 영역

▲ 프로세스의 메모리 구조

C++과 같은unmanaged language 프로그램을 만든다는 것은 프로그래머가 직접 Stack과 Heap 등의 메모리를 관리한다는 것이다. 이번 글에서는 Stack과 Heap에 대해서 자세히 다뤄본다.

스택(Stack)

C++에서 프로그래머는 다음과 같이 변수를 선언하여 메모리에 데이터를 저장할 수 있다.

int main()
{
    int a = 3;
    int b = 4;
    int c = a + b;

    return 0;
}

이 프로그램은 a라는 메모리 공간에 3을 저장하고, b라는 메모리 공간에 4를 c라는 메모리 공간에 a+b를 저장한다. 하지만 컴파일러는 변수명을 기억할 수 없다. 컴파일러는 메모리 stack의 인덱싱을 통해서 각 변수에 접근할 수 있다. 먼저 main 함수가 메모리에 로드되면 main함수의 지역변수들이 다음과 같이 순차적으로 stack에 쌓이게 된다.

▲ Stack의 단순화 모습


그리고 컴파일러는 변수명이 아닌 stack의 top에서부터 인덱싱을 통해서 원하는 변수에 접근하게 된다. 즉, a라는 변수는 top에서 부터 두 칸, b라는 변수는 top에서 부터 한 칸 떨어져 있음을 통해서 각 변수들의 메모리 공간에 접근할 수 있는 것이다.

Stack frame과 Call stack

프로그램에서 함수가 실행되면 메모리 stack에 그 함수에 대한 정보가 쌓이게 된다. 이 stack을 Call stack이라 부른다. 그리고 call stack에 함수에 대한 정보들은 다음과 같다.

  • 매개변수(arguments)
  • 지역변수(local variable)
  • 반환 주소(return address)
  • (C++에서 멤버 함수일 경우) class의 멤버 변수에 접근하기 위한 this 포인터 주소

함수가 호출되면 이 정보들이 한 번에 call stack에 쌓이게 되는데 이 것을 stack frame이라 한다. 그리고 함수가 종료되면 그 함수의 stack frame은 한번에 stack에서 지워지게 된다. 이러한 스택 구조의 메모리로 인해서 매개변수와 지역변수의 수명은 해당 함수의 종료지점으로 정해진다. 그리고 스택 구조 덕분에 메모리 스택의 상태는 함수 호출 이전으로 안전하게 돌아갈 수 있게 된다.

▲ Stack frame의 구성

아래와 같은 소스코드를 실행했을 때 call stack에 변수들이 어떻게 저장되는지 살펴보자.

void funcA()
{
    float floatA;
}
void funcB(short a )
{
    short shortA;
    void funcA();
}
int main()
{
    int intA;

    funcB();

    return 0;
}

먼저 main() 함수가 실행되면 main함수의 stack frame이 메모리에 로드된 후 순차적으로 funcB()의 stack frame이 다음으로는 funcA()의 스택 프레임이 call stack에 로드된다.

▲ call stack에서 stack frame이 쌓이는 모습

함수의 실행이 끝나면 funcA() 함수의 stack frame부터 순차적으로 반환되며 call stack에서 해제된다.

▲ call stack에서 stack frame이 해제되는 모습

지금까지 Stack 메모리에 대해서 알아보았다. 다음 글에서는 Heap에 대해서 다룬다.

 


참조:

유튜브 채널 - 코드없는 프로그래밍: https://www.youtube.com/channel/UCHcG02L6TSS-StkSbqVy6Fg