BioPlayground

🧬
목록으로

동적 메모리 할당 — malloc과 free

프로그램 실행 중에 메모리를 동적으로 할당하는 이유, 스택과 힙의 차이, 메모리 누수가 왜 위험한지 배웁니다.

중급
|
10
|
검증 완료 (2026-07)
동적 메모리mallocfree힙 메모리스택 메모리메모리 누수
진행률0/13 (0%)

동적 메모리 할당 — malloc과 free

이 토픽을 마치면

스택과 힙 메모리의 차이, 동적 할당이 왜 필요한지, 그리고 메모리 누수가 왜 발생하는지 이해합니다.


두 종류의 메모리

프로그램이 쓰는 메모리는 크게 **스택(Stack)**과 힙(Heap) 두 영역으로 나뉩니다.

text
높은 주소 ┌──────────────┐
          │    스택       │ ← 함수 호출할 때마다 자동 할당/해제
          │   ↓ 아래로    │
          │              │
          │              │
          │   ↑ 위로     │
          │    힙        │ ← 프로그래머가 직접 할당/해제
낮은 주소 └──────────────┘
스택
할당자동 (함수 호출 시)수동 (malloc)
해제자동 (함수 종료 시)수동 (free)
크기작고 고정적 (보통 수 MB)크고 유동적 (RAM 한도)
속도매우 빠름약간 느림
용도지역 변수, 함수 인자크기가 미정인 데이터

왜 동적 할당이 필요한가

스택에 변수를 만들면 크기를 컴파일 시점에 알아야 합니다:

c
int arr[100];  // 100개 — 컴파일할 때 결정

하지만 사용자가 "몇 개의 데이터를 입력할 건가요?"라고 물으면, 프로그램이 실행되기 전에는 알 수 없습니다. 이럴 때 동적 할당을 씁니다:

c
int n;
printf("몇 개? ");
scanf("%d", &n);

int *arr = malloc(n * sizeof(int));  // 실행 중에 n개만큼 할당

malloc(memory allocation)은 힙에서 요청한 바이트만큼 메모리를 가져옵니다.


malloc과 free

c
#include <stdlib.h>

// 할당
int *data = malloc(5 * sizeof(int));

// 사용
for (int i = 0; i < 5; i++) {
    data[i] = i * 10;
}

// 해제
free(data);
data = NULL;  // 안전을 위해 NULL로 설정

malloc이 메모리를 주면 쓸 수 있고, 다 쓰면 free로 돌려줘야 합니다. free를 안 하면 그 메모리는 프로그램이 끝날 때까지 반환되지 않습니다.


메모리 누수

c
void process() {
    int *data = malloc(1000000 * sizeof(int));
    // 데이터 처리...

    if (error) {
        return;  // free 안 하고 빠져나감!
    }

    free(data);
}

이 함수가 에러로 빠져나가면 free가 실행되지 않습니다. 100만 개의 int(약 4MB)가 돌아오지 않습니다. 이 함수가 1000번 호출되면 4GB가 새어나갑니다. 이것이 **메모리 누수(memory leak)**입니다.

메모리 누수는 프로그램이 즉시 죽지 않아서 위험합니다. 서서히 메모리를 먹다가 시스템 전체가 느려지고, 결국 OOM(Out of Memory)으로 죽습니다. 서버 프로그램에서는 며칠 뒤에 갑자기 죽는 원인이 되기도 합니다.


고급 언어의 해결 — 가비지 컬렉션

Python, JavaScript, Java, Go는 **가비지 컬렉터(GC)**가 알아서 해제합니다:

python
def process():
data = [0] * 1000000
# 함수 끝나면 data를 참조하는 변수가 없어짐
# → GC가 자동으로 메모리 회수

프로그래머가 free를 호출할 필요가 없습니다. GC가 "이 메모리를 가리키는 변수가 아무도 없네?"를 주기적으로 검사하고 회수합니다.

대가가 있습니다:

수동 (C)자동 (Python/Java)
메모리 누수 가능성높음 (프로그래머 실수)낮음 (GC가 처리)
성능예측 가능GC 실행 시 잠깐 멈출 수 있음
코드 복잡도높음 (free 관리)낮음 (신경 안 써도 됨)

Rust는 제3의 길을 택했습니다 — GC도 없고 수동 free도 없이, 컴파일러가 변수의 수명을 추적해서 자동으로 해제 코드를 넣어줍니다.


핵심

스택은 자동으로 할당/해제되고, 은 프로그래머가 직접 관리합니다. malloc으로 힙에 할당하고, free로 반환 — 빠뜨리면 메모리 누수. 고급 언어는 GC로 이 문제를 자동 해결하지만, 왜 메모리 관리가 필요한지 아는 것이 CS의 기초입니다.