C언어 동적 메모리 할당 마스터하기

정지효·2025년 4월 26일
0

C언어의 매력은 매모리를 직접 관리할 수 있다는 점이다.
하지만 자유도가 높은 만큼 메모리 누수, 이중 해제, 댕글링 포인터, 힙 오버플로우 같은 문제들도 동적 메모리 관리 실수에서 시작된다. 이번 글에서는 필자가 직접 C언어 개발을 하며 터득한 메모리 관리 방법을 설명하도록 하겠다.


malloc(), free()

malloc()

malloc은 사이즈만큼의 연속된 메모리 블록을 요청한다.
성공시 할당된 메모리의 시작 주소를 반환하고, 실패시 Null값은 반환한다.
실제 시스텀에서는 malloc() 호출시 내부적으로 힙 영역을 사용하며, 필요하면 커널에 brk() 또는 mmap() 시스템콜을 요청한다.

free()

malloc 또는 realloc으로 받은 메모리를 반환한다. 이후 해당 포인터는 사용이 불가능하다.

주의점

이때 해제를 잘 하지 않으면 메모리 누수가 발생하게 된다. 또한 같은 포인터를 두번 해제하거나 이미 해제된 포인터를 사용하면 에러가 발생하게 되니 이 점을 주의해야한다.


realloc(), calloc()

realloc()

realloc은 이미 할당된 메모리 블록의 크기를 늘리거나 줄인다. 내부적으로 새 블록을 할당하고, 데이터를 복사 후 기존 블록을 해제할 수도 있다. 만약 realloc이 실패하면 Null을 반환하지만 기존 블록은 그대로 남아있다.

calloc()

calloc은 malloc과 같지만 할당된 값을 0으로 초기화 시켜 보안적으로 안전한 초기값을 보장한다는 특징이 있다.


malloc의 내부 동작(glibc)

malloc 함수는 크게 다음과 같은 흐름으로 동작한다.

  • 128kb 이하 요청은 fast bins, small bins에서 처리.
  • 그것보다 큰 요청은 mmap()을 지접 호출해서 별도의 메모리 영역을 할당한다.
  • 힙 확장 필요시 sbrk()호출하여 힙 영역을 키운다.

이 내용에 대해서는 다른 글로 더 자세하게 설명하도록 하겠다.


메모리 단편화

외부 단편화

외부 단편화는 여러 개의 작은 해제 블록이 흩어져 있어 총 공간은 충분해도 큰 연속 블록을 만들 수 없는 상황이다.

내부 단편화

요청한 크기보다 더 큰 블록을 할당받은 경우에 남는 공간이 낭비되는 상황이다.

단편화를 줄이는 기법

메모리의 단편화가 생기면 메모리를 낭비하게 되기 때문에 이러한 단편화를 줄여야 한다.
단편화를 줄이는 대표적인 기법은 메모리 풀, 슬랩 할당자, 버디 시스템 등이 있다.

  • 메모리 풀: 같은 크기의 객체를 미리 할당해 풀로 관리
  • 슬랩 할당자: 커널 수준에세 사용되는 고급 메모리 풀
  • 버디 시스템: 2의 거듭제곱 크기의 블록을 나눠서 관리

메모리 풀

메모리 풀은 실제로 고성능 서버, 게임엔진 등에서 필수적으로 사용된다.
동적할당 대신 미리 만들어둔 메모리 덩어리에서 빠르게 꺼내 쓰고 반납하는 것이다.
다음은 간단한 메모리 풀 예시이다.

#define POLL_SIZE 1024

char polll[POLL_SIZE];
size_t offset = 0;

void* pool_alloc (size_t size){
	if (offset + size > POLL_SIZE) return NULL;
    void* ptr = &pool[offset];
    offset += size;
   	return ptr;
}

메모리 오류 잡는법

Address Sanitizer(ASan)

컴파일할때 -fsanitize=address 옵션을 추가하면 런타임 메모리 오류를 즉시 탐지한다.

gcc -fsanitize=address -g myprogram.c -o myprogram
./myprogram

해당 옵션으로 컴파일을 하면 use after free, heap buffer overflow, stack buffer overflow, Memory Leak 같은 버그들을 컴파일러가 잡아준다.

valgrind

valgrind --leak-check+full ./myprogram
발그라인드를 사용하면 런타임 메모리 오류와 누수를 감지한다.
하지만 실행 속도가 느리다.


그럼 필자는 다음 글로 돌아오겠다.

profile
SRIHS infoSec 119th

0개의 댓글