스택 메모리 (Stack Memory)

Jaemyeong Lee·2024년 8월 2일

게임 서버1

목록 보기
22/220

이 Step에서 다루는 것

  • 스택이 “함수들이 같이 쓰는 임시 공간”이라는 말이 정확히 무슨 뜻인지
  • 함수 호출/리턴이 일어날 때 스택이 어떻게 쌓이고(호출) 어떻게 정리되는지(리턴)
  • 스택 오버플로우가 왜/언제 터지는지, 그리고 디버깅에서 어떻게 보이는지

학습 목표

  • 스택을 한 문장으로 정의할 수 있다.
  • LIFO와 “주소 방향(높은→낮은)”을 헷갈리지 않는다.
  • 스택 오버플로우의 대표 원인 2가지를 말할 수 있다. (무한 재귀 / 큰 지역 변수)

스택이란?

  • 함수 호출 과정에서 “잠깐 필요한 정보”를 쌓아두는 메모리 영역입니다.
  • 스택에 들어가는 대표 정보:
    • 반환 주소(Return Address): 함수가 끝나면 “어디로 돌아갈지”
    • 매개변수(Parameter): 호출 시 넘긴 값(대부분 “복사”)
    • 지역 변수(Local): 함수 내부에서 만든 임시 데이터
  • LIFO(후입선출): 가장 나중에 들어온(가장 마지막에 호출된) 함수의 정보가 가장 먼저 정리됩니다.
  • 주소 방향(개념): 보통 높은 주소 → 낮은 주소 방향으로 “쌓인다”라고 설명합니다.
  • 크기: 일반적으로 프로그램 시작 시 일정 크기를 잡아두고(수 MB 단위가 흔함), 호출/리턴에 따라 늘었다 줄었다 합니다.

스택을 그림으로 잡으면 이런 느낌입니다.

높은 주소
┌──────────────────────────────┐
│ main()의 스택 프레임          │
├──────────────────────────────┤
│ A()의 스택 프레임             │  ← A() 호출
├──────────────────────────────┤
│ B()의 스택 프레임             │  ← B() 호출 (가장 최근)
└──────────────────────────────┘
낮은 주소

리턴 순서: B() → A() → main()

자주 하는 오해 2가지

  • “리턴하면 메모리가 0으로 초기화되나요?”
    보통은 아닙니다. “사용하지 않는 영역”으로만 바뀌고, 값은 남아 있을 수 있습니다. 그래서 초기화 없이 읽으면 “쓰레기 값”이 나올 수 있습니다.
  • “스택은 영구 저장소인가요?”
    절대 아닙니다. 함수가 끝나면 해당 영역은 더 이상 내 것이 아니고, 다음 호출에서 덮어써질 수 있습니다.

비유: 슈퍼마켓 계산대

스택을 “공용 메모장 1권”으로 비유하면 이해가 빨라집니다.

  • 메모장(스택)은 하나고, 여러 사람이(함수들이) 순서대로 씁니다.
  • 어떤 함수가 자기 일을 하려면, 먼저 “여기부터 여기까지 내 자리”라고 경계선을 긋고 사용합니다.
  • 다른 함수가 호출되면(새 손님), 앞사람이 쓰던 자리 아래에 새로 경계선을 긋고 이어서 씁니다.
  • 일이 끝나면(리턴), 지우고 정리하는 게 아니라 “그 사람의 자리”를 반납해서 이전 사람이 계속 쓰게 됩니다.
main()이 쓰다가
┌───────────────┐
│ main()        │  ← 여기까지 사용
└───────────────┘

main()이 A()를 호출하면
┌───────────────┐
│ main()        │  (그대로)
├───────────────┤
│ A()           │  ← A()가 추가로 사용
└───────────────┘

A()가 B()를 호출하면
┌───────────────┐
│ main()        │
├───────────────┤
│ A()           │
├───────────────┤
│ B()           │  ← 가장 최근 호출
└───────────────┘

B()가 끝나면 B() 자리만 반납 → A()로 복귀
  • 핵심 결론: 스택은 “한 번 쓰고 버리는 임시 공간”에 가깝습니다.
    (강의 비유대로라면 “인터스텔라 웨이브”처럼 언제든 덮어쓰일 수 있는 불안정한 공간)

스택 오버플로우 (Stack Overflow)

  • 스택에 쌓이는 데이터(스택 프레임)가 너무 깊거나(너무 많이 호출), 너무 크면(지역 변수가 너무 큼) 스택 공간을 넘어서면서 터집니다.
  • 대표 원인:
    • 무한 재귀 / 종료 조건 누락
    • 너무 큰 지역 변수(특히 큰 배열) 를 함수 내부에 생성

무한 재귀로 터지는 예시

void RecursiveFunction() {
    RecursiveFunction();
}

int main() {
    RecursiveFunction();  // 스택 오버플로우 발생
}
  • 무한 재귀 호출로 스택 프레임이 계속 쌓이고, 한계를 넘는 순간 프로그램이 강제 종료(크래시) 됩니다.

“종료 조건이 있는 재귀”의 최소 형태

void RecursiveFunction(int depth)
{
    if (depth <= 0)
        return;

    RecursiveFunction(depth - 1);
}

디버깅 힌트

  • 스택 오버플로우가 의심될 때는 디버거의 Call Stack(호출 스택) 을 보면,
    같은 함수가 끝없이 반복되는 형태로 쌓여 있는 경우가 많습니다.

profile
李家네_공부방

0개의 댓글