[C언어] 힙 메모리 동적 배열(IntList)의 구조와 할당 크기 계산 실수로 인한 Heap Buffer Overflow 발생 원인

자신감·2026년 9월 28일

5주차

목록 보기
8/11

1. 정적 배열과 동적 배열의 물리적 차이

  • 정적 배열 (int arr[10];): 프로그램을 만들 때 크기를 10개로 고정합니다. 프로그램을 켜서 실행하는 도중에 11개로 크기를 늘릴 수 없습니다.
  • 동적 배열: 처음에는 작은 크기로 시작했다가, 데이터가 꽉 차면 실행 중에 운영체제에 요청하여 더 큰 메모리를 받아와 크기를 늘립니다.

2. 동적 배열을 만드는 구조체(IntList)의 3가지 멤버 변수

typedef struct {
    int    *data;  // 힙 메모리에 만든 배열의 시작 주소 번호 (8바이트)
    size_t  len;   // 현재 배열에 실제로 들어있는 숫자 개수 (정수)
    size_t  cap;   // 현재 할당받은 메모리에 최대 담을 수 있는 숫자 개수 (정수)
} IntList;
  1. data: 숫자들이 저장된 힙 메모리의 시작 주소 번호입니다.
  2. len: 현재까지 사용자가 배열에 집어넣은 숫자의 개수입니다.
  3. cap (Capacity, 용량): 현재 운영체제로부터 받아둔 메모리에 숫자를 최대 몇 개까지 넣을 수 있는지를 적어둔 숫자입니다.

3. 스택(Stack)과 힙(Heap) 메모리의 차이

  • 스택 (Stack): IntList list;처럼 함수 안에서 만든 변수 자체가 자리 잡는 메모리 영역입니다. 함수가 끝나면 자동으로 사라집니다.
  • 힙 (Heap): malloc() 함수를 호출했을 때 운영체제가 따로 떼어주는 메모리 영역입니다. 사용자가 직접 해제하기 전까지 계속 유지됩니다.
  • 구조체 변수 list는 스택에 있고, list.data 안에 적힌 주소가 가리키는 실제 숫자 공간들은 힙에 있습니다.

4. 원소가 꽉 찼을 때 realloc()으로 크기를 늘리는 과정

static void list_push(IntList *l, int x) {
    // 1단계: 현재 개수(len)와 최대 용량(cap)이 같은지 검사
    if (l->len == l->cap) {
        size_t newcap = l->cap * 2; // 용량을 2배로 계산 (예: 8개 -> 16개)
        
        // 2단계: realloc으로 16개 * 4바이트(int 크기) = 64바이트 힙 메모리를 새로 확보
        int *p = realloc(l->data, newcap * sizeof(int));
        if (p == NULL) {
            exit(1); // 메모리 부족 시 종료
        }
        
        l->data = p;      // 새 주소로 변경
        l->cap = newcap;  // 최대 용량을 16으로 변경
    }

    // 3단계: 데이터를 넣고 개수를 1 증가시킴
    l->data[l->len] = x;
    l->len++;
}

5. Heap Buffer Overflow(힙 버퍼 오버플로우)가 발생하는 기계적 원인

이 오류는 변수에 적어둔 용량 숫자(cap)와 실제 힙에 빌려둔 물리 바이트 크기가 맞지 않을 때 일어납니다.

  1. 개발자가 실수로 sizeof(int)(4바이트)를 곱하지 않고 realloc(l->data, 16)을 실행했다고 가정합니다.
  2. 컴퓨터는 int 16개(64바이트)가 아니라, 오직 16바이트(int 4개 분량)만 힙에 할당합니다.
  3. 하지만 구조체 변수에는 l->cap = 16;이라고 적혀 있습니다.
  4. list_push 코드는 cap이 16이므로 5번째, 6번째, 7번째 숫자를 넣을 때도 공간이 충분하다고 판단하여 힙 메모리에 값을 씁니다.
  5. 이때 4개 크기를 넘어서는 위치(남의 메모리 영역)에 숫자를 덮어쓰게 됩니다.
  6. 이 현상을 Heap Buffer Overflow라고 부르며, 다른 데이터가 깨지거나 프로그램이 강제 종료됩니다.

6. 결론 요약

  • cap 변수 값은 실제 운영체제로부터 할당받은 int 개수와 반드시 일치해야 합니다.
  • malloc이나 realloc을 호출할 때는 반드시 원소 개수에 데이터 1개의 크기(sizeof(int))를 곱한 총 바이트 수를 넘겨야 메모리 침범이 발생하지 않습니다.
profile
잘할 수밖에 없는 자신감

0개의 댓글