오늘은 malloc이 무엇인지, 어떤 식으로 작동하는지, malloc과 동적 메모리 할당에 대해서 알아보도록 하자
운영체제 관점에서 메모리는 크게 4영역으로 나뉘어 있음:

malloc, free 할 때 쓰는 영역malloc이나 calloc을 사용해서 실행 중에 필요한 만큼 메모리를 확보할 수 있다!int n;
scanf("%d", &n); // 사용자 입력에 따라 크기 결정
int *arr = malloc(n * sizeof(int));
int big_array[1000000]; // 스택 오버플로우 가능
⬇⬇⬇
int *big_array = malloc(1000000 * sizeof(int)); // 힙에서는 OK!
typedef struct Node {
int val;
struct Node *next;
} Node;
Node *head = malloc(sizeof(Node)); // 노드 하나 생성
malloc과 free를 잘 활용하면 메모리를 절약해서 쓸 수 있음.할당기는 다음 제약 조건들을 충족해야 한다:
malloc, free가 호출될지 모름.이 경우, 할당 자체는 new 같은 연산자로 요청하지만, 해제는 명시하지 않으므로 "묵시적"
malloc/free, new/delete 사용.free를 안 하면 메모리 누수(leak)가 발생하고, 잘못된 포인터를 해제하면 정의되지 않은 동작이 발생할 수 있음.| 구분 | 의미 | 관련 언어 예시 |
|---|---|---|
| 묵시적 할당기 (사용자 관점) | 메모리 해제를 자동으로 수행 (GC 사용) | Java, Python, Go 등 |
| 명시적 할당기 (사용자 관점) | 메모리 해제를 직접 수행 | C, C++ 등 |
| 묵시적/명시적 자유 리스트 (할당기 내부) | 자유 블록 연결 방식의 차이 | C의 malloc 구현 방식에서 사용 |
malloc과 free 함수malloc(size_t size) 함수는 힙에서 size 바이트의 메모리를 할당해주는 함수이며, 성공 시 해당 메모리의 포인터를 반환.free(void *ptr)는 이전에 malloc으로 할당받은 메모리를 해제.malloc으로 할당한 메모리는 자동으로 해제되지 않음.
프로그램이 끝날 때까지 계속 남아 있어.
그래서 명시적으로 free 해줘야 해.
int *arr = malloc(100 * sizeof(int));
// 사용 끝났으면
free(arr); // 다시 사용할 수 있게 반환!
malloc은 할 때마다 힙에서 메모리를 계속 소비해.
free를 안 하면 → 점점 힙이 커지고 → 결국 메모리 부족 발생! (특히 긴 시간 실행되는 프로그램에서 치명적)
OS가 "이 앱 메모리 너무 많이 씀!" 하면서 터뜨릴 수도 있어.
for (int i = 0; i < 1000; i++) {
int *temp = malloc(1000); // 계속 할당만 하고
// free(temp); 안 하면? → 누수 발생!
}
free된 메모리는 다음 malloc 요청 시 재사용 가능.
메모리를 아끼고 효율 높일 수 있음.
free(p)를 호출한 순간부터, 그 포인터 p가 가리키는 메모리는 해제되고 더 이상 유효하지 않은 상태가 됨.
이후에 그 메모리 영역은 할당기 내부적으로 다른 목적으로 재사용될 수 있음.
p = 10 같은 접근을 하면 정의되지 않은 동작(Undefined Behavior) 이 발생. 즉, 어떤 일이 벌어질지 아무도 보장 못함.
int *p = malloc(4 * sizeof(int));
free(p);
p[0] = 123; // ❌ 매우 위험!
이건 이미 반환된 메모리에 접근하는 것이므로 메모리 오류, 보안 취약점, 또는 운 좋게 조용히 작동할 수도 있지만 전혀 신뢰할 수 없음.
int *p = malloc(4 * sizeof(int));
free(p);
p = NULL; // 💡 이제 p는 더 이상 잘못된 메모리를 가리키지 않음
free 후에 해당 포인터를 NULL로 초기화하는 건 매우 좋은 습관.
나중에 잘못 접근하려 해도 p는 프로그램을 즉시 종료시켜 버그를 빨리 발견할 수 있게 해준다.
즉, free한 포인터는 절대 재사용 ㄴㄴ.
그 메모리 주소가 살아있는 것 같아도, 이제 니 메모리 아님
calloc(size_t nmemb, size_t size)nmemb개의 요소 각각 size 바이트 크기의 메모리 블록을 할당하고, 0으로 초기화합니다.malloc(nmemb * size)와 memset(ptr, 0, nmemb * size)를 합친 기능이에요.int *arr = (int *)calloc(10, sizeof(int)); // 10개 정수 공간을 0으로 초기화malloc/calloc 등으로 할당한 블록의 크기를 조정할 때 사용.free하는 동작일 수도 있음.int *arr = (int *)malloc(5 * sizeof(int));
// ...
arr = (int *)realloc(arr, 10 * sizeof(int)); // 더 큰 배열로 확장⚠ realloc은 새 블록이 할당될 경우, 기존 포인터는 무효가 되니 반드시 반환값을 저장해야 함.
| 함수 | 기능 | 초기화 여부 |
|---|---|---|
malloc(size) | size 바이트 메모리 할당 | ❌ 초기화 안 됨 |
calloc(n, size) | n * size 바이트 메모리 할당 | ✅ 0으로 초기화 |
realloc(ptr, size) | ptr이 가리키는 블록의 크기 변경 | ❌ 새로 늘어난 부분은 초기화 안 됨 |
블록은 할당됐지만, 실제로는 일부만 사용되고 나머지는 낭비되는 경우
- 예: 17바이트 요청했는데, 24바이트 블록이 할당됨 → 7바이트 낭비
int *p = malloc(17); // 실제로는 8의 배수로 24바이트 할당될 수도 있음
메모리에는 충분한 여유 공간이 있음에도, 연속된 큰 블록이 없어서 할당이 안 되는 상황
- 예: 힙에 100바이트 여유가 있지만, 40, 30, 30으로 흩어져 있어 → 90바이트 요청하면 실패!
| 이유 | 설명 |
|---|---|
| 메모리 낭비 | 전체 사용률이 떨어져 프로그램이 더 많은 메모리를 요구하게 됨 |
| 할당 실패 | 여유는 있지만 “연속된 공간”이 없어 메모리 할당에 실패할 수 있음 |
| 성능 저하 | 단편화가 심하면 할당/해제 작업 자체도 느려질 수 있음 |