C언어에서 동적 메모리 할당의 기본 개념, malloc()과 free() 함수 사용법을 알아보려고 한다.
정보처리기사 실기에서 자주 출제되는 동적 메모리 할당 문제를 풀기 위해서 핵심 개념을 아래에 표로 정리를 했다.
| 개념 | 설명 | 예시 |
|---|---|---|
| malloc() | 힙 메모리에서 지정한 크기만큼 할당 | malloc(sizeof(int)) |
| sizeof() | 자료형의 크기(바이트)를 반환 | sizeof(int)=4 |
| 형변환() | malloc 반환값을 원하는 포인터 타입으로 변환 | (int*)malloc(...) |
| free() | 할당된 메모리를 해제 | free(p); |
동적 메모리 할당은 프로그램 실행 중에 필요한 만큼
메모리를 할당받는 방법이라고 볼 수 있다.
일반 변수는 스택(Stack)에 지정되고 크기가 컴파일 시점에 결정된다.
반면 동적 메모리는 힙(Heap)에 저장되고 런타임에 크기를 결정할 수 있다.
- 프로그램이 실행하면 운영체제는 해당 프로그램을 위해 메모리 공간을 할당해준다.
- 메모리 구조는 4가지의 영역으로 나뉘게 되는데
CODE 영역, DATA 영역, HEAP 영역, STACK 영역 등으로 나뉘게 된다.
일반 변수는 스택(Stack)에 저장되고, 크기가 컴파일 시점에 결정된다.
반면 동적 메모리는 힙(Heap)에 저장되고 런타임에 크기를 결정할 수 있다.
스택은 크기가 고정되어 있지만, 힙은 malloc( )으로 필요한 만큼 할당받을 수 있다.
| 구분 | 스택 메모리 | 힙 메모리 |
|---|---|---|
| 할당 시점 | 컴파일 시 | 실행 시 |
| 크기 결정 | 고정 | 가변 |
| 해제 | 자동 (함수 종료 시) | 수동(free 호출) |
| 예시 | int arr[10]; | malloc(sizeof(int)*10) |
malloc( )은 Memory Allocation의 약자로,
힙 메모리에서 지정한 바이트만큼 공간을 할당한다. 사용하려면 #include<stdlib.h>가 필요하다.
#include <stdlib.h> // malloc, free 사용 시 필요
int *p = (int*)malloc(sizeof(int)); // int 1개 크기 할당 크기 할당
malloc은 요청한 크기만큼 힙에서 메모리를 할당하고, 그 주소를 반환한다.
포인터 = (자료형*)malloc(sizeof(자료형));
// 예시
int *p = (int*)malloc(sizeof(int)); // int 1개
int *arr = (int*)malloc(sizeof(int) * 10) // int 10개 (배열)
sizeof( )는 자료형이나 변수의 크기를 바이트 단위로 반환한다.
| 자료형 | sizeof() 결과 |
|---|---|
| char | 1바이트 |
| int | 4바이트 |
| float | 4바이트 |
| double | 8바이트 |
| 포인터 | 4 또는 8바이트(시스템에 따라) |
// 구조체의 크기도 계산 가능
struct node {
char c;
struct node* p;
};
malloc(sizeof(struct node)); // 구조체 1개 크기 할당
malloc()은 void 타입으로 반환하므로,
사용하려는 포인터 타입으로 형변환해야 한다.
// void*를 int*로 형변환
int *p = (int*)malloc(sizeof(int));
// void*를 struct node*로 형변환
struct node *n = (struct node*)malloc(sizeof(struct node));
// 이중 포인터로 형변환 (2차원 배열용)
int **arr = (int**)malloc(sizeof(int*) * rows);
여기서 형 변환을 꼭 잊지 말아야 한다.
malloc의 반환값을 형변환하지 않으면 경고가 발생할 수 있다.
(int*), (char*), (struct node*)등 알맞은 타입으로 변환해야 한다.
free()는 malloc()으로 할당한 메모리를 해제한다.
해제하지 않으면 메모리 누수(Memory Leak)가 발생한다.
int *p = (int*)malloc(sizeof(int));
*p = 100;
// 사용 완료 후 해제
free(p);
p = NULL; // 해제 후 NULL로 초기화 (권장)
free() 후에 포인터를 사용하면 안된다.
해제된 메모리를 다시 사용하게 된다면 오류가 발생하므로
free() 후에는 포인터를 NULL로 초기화해야 한다.