Krafton Jungle/2. Keywords
[WEEK05] malloc, calloc, realloc
munsik22
2025. 4. 11. 17:01
C 언어에서 동적 메모리를 다룰 때 가장 자주 사용하는 함수들인 malloc, calloc, realloc에 대해 정리해보자.🧐
📦 malloc() : 메모리 할당
개념
- malloc은 메모리 블록을 원하는 크기만큼 할당해 주는 함수다.
- 초기화는 하지 않고, 메모리 주소만 반환한다.
문법
void* malloc(size_t size);
- size: 할당할 바이트 수
- 반환값: 성공 시 할당된 메모리의 시작 주소, 실패 시 NULL
예시
int* ptr = (int*)malloc(sizeof(int) * 5); // int 5개 분량
💻 예제
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(sizeof(int) * n); // int 5개 할당
if (arr == NULL) {
printf("메모리 할당 실패\n");
return 1;
}
printf("malloc으로 할당된 메모리 초기값:\n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]); // 초기화되지 않은 값 (쓰레기 값)
}
free(arr);
return 0;
}
⚠️ 주의
- 할당된 메모리는 초기화되지 않음 (쓰레기 값)
- 꼭 NULL 체크 필요!
📦 calloc() : 메모리 할당 + 0 초기화
개념
- calloc은 malloc과 비슷하지만, 초기화까지 함께 진행한다.
- 0으로 채워진 메모리 블록을 할당받는다.
문법
void* calloc(size_t num, size_t size);
- num: 요소 개수
- size: 요소 하나당 크기
- 반환값: 성공 시 메모리 주소, 실패 시 NULL
예시
int* ptr = (int*)calloc(5, sizeof(int)); // int 5개, 0으로 초기화됨
💻 예제
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)calloc(n, sizeof(int)); // int 5개, 0으로 초기화
if (arr == NULL) {
printf("메모리 할당 실패\n");
return 1;
}
printf("calloc으로 할당된 메모리 초기값:\n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]); // 0 출력됨
}
free(arr);
return 0;
}
차이점 요약
| 항목 | malloc() | calloc() |
| 초기화 | 안 됨 (쓰레기값) | 0으로 초기화됨 |
| 인자 | 바이트 크기 1개 | 요소 개수 + 요소 크기 |
🔄 realloc() : 메모리 크기 변경
개념
- 이미 할당된 메모리 블록의 크기를 변경할 때 사용한다.
기존 데이터를 보존하면서 크기를 늘리거나 줄인다.
문법
void* realloc(void* ptr, size_t new_size);
- ptr: 기존에 할당한 메모리 포인터
- new_size: 변경할 총 크기 (바이트 단위)
- 반환값: 새 메모리 주소, 실패 시 NULL (주의: 실패하면 기존 메모리는 유지됨)
예시
int* ptr = (int*)malloc(sizeof(int) * 3); // int 3개
ptr = (int*)realloc(ptr, sizeof(int) * 6); // int 6개로 확장
💻 예제
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 3;
arr = (int*)malloc(sizeof(int) * n); // 처음에 3개 할당
if (arr == NULL) {
printf("메모리 할당 실패\n");
return 1;
}
// 초기 값 입력
for (int i = 0; i < n; i++) {
arr[i] = (i + 1) * 10;
}
// 크기 확장 (3 → 6)
int* temp = (int*)realloc(arr, sizeof(int) * 6);
if (temp == NULL) {
printf("메모리 재할당 실패\n");
free(arr); // 기존 메모리 해제
return 1;
}
arr = temp;
// 새 공간 초기화
for (int i = n; i < 6; i++) {
arr[i] = (i + 1) * 10;
}
printf("realloc 후 배열 내용:\n");
for (int i = 0; i < 6; i++) {
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
⚠️ 주의
- realloc이 새 메모리를 할당하면, 기존 포인터는 유효하지 않을 수 있음
- 실패 시 NULL 반환하므로, 다음처럼 안전하게 쓰는 것이 좋음:
int* tmp = realloc(ptr, new_size);
if (tmp != NULL) {
ptr = tmp;
} else {
// 기존 ptr 그대로 두고 에러 처리
}
📌 요약 비교표
| 함수 | 기능 | 초기화 | 인자 | 특징 |
| malloc | 메모리 할당 | ❌ | 전체 크기 | 가장 기본적인 함수 |
| calloc | 할당 + 초기화 | ✅ (0으로) | 개수, 크기 | 안전한 초기화 제공 |
| realloc | 크기 변경 | 기존 유지 | 기존 포인터, 새 크기 | 확장/축소 시 유용 |
💡 Tip
- malloc과 calloc은 할당할 때만 사용하고,
- realloc은 이미 할당된 메모리의 크기를 변경할 때 사용하자.
- calloc은 보안, 안정성 측면에서 malloc보다 더 안전한 경우가 많다 (초기화되기 때문에).
위에서 calloc()이 malloc()보다 더 안전한 경우가 많다고 했는데, 그 이유는 크게 두 가지 관점에서 설명할 수 있다.
1. 초기화 때문 : 예기치 않은 쓰레기값 방지
✅ malloc()은 메모리만 할당하고 초기화는 하지 않음
- 즉, 해당 메모리에는 기존에 다른 프로그램이나 시스템이 사용하던 임의의 데이터(쓰레기값)가 그대로 남아 있을 수 있어요.
- 이 상태에서 실수로 값을 읽으면 예상치 못한 동작이나 버그가 발생할 수 있다.
int* arr = (int*)malloc(sizeof(int) * 5);
printf("%d\n", arr[0]); // 초기화 안 해서 쓰레기값 출력 가능
✅ calloc()은 0으로 초기화된 메모리를 할당
- 즉, 모든 비트가 0으로 세팅됨
- 숫자 타입은 0, 포인터는 NULL, 플래그는 false 등으로 해석됨
- 예측 가능한 값이므로 디버깅과 안정성 면에서 유리함
int* arr = (int*)calloc(5, sizeof(int));
printf("%d\n", arr[0]); // 항상 0
2. 보안 측면에서도 더 안전
초기화되지 않은 메모리를 그대로 사용하는 것은 다음과 같은 보안 이슈를 유발할 수 있다.
- 정보 노출Information Leakage : 초기화되지 않은 메모리에는 이전 프로세스가 남긴 민감한 데이터(비밀번호, 토큰 등)가 있을 수 있음
- 비결정성 문제Nondeterministic bugs : 프로그램이 환경에 따라 다르게 동작하게 되어, 테스트 시는 괜찮다가 실제 배포에서 터질 수 있음
- Undefined Behavior : 값이 불확실하므로, 예측 불가능한 버그의 원인이 되기 쉽고, 디버깅도 매우 어려움
정리
| 상황 | calloc()이 더 안전한 이유 |
| 값을 읽기 전에 초기화가 꼭 필요할 때 | 0으로 자동 초기화 |
| 포인터 배열을 다룰 때 | NULL로 초기화되어 오류 가능성 낮음 |
| 보안에 민감한 환경 (예: 입력 검증, 암호 처리 등) | 이전 정보 유출 방지 |
| 버그 추적이 어려운 대규모 프로젝트 | 값 예측이 가능해서 디버깅 용이 |
- malloc()은 빠르지만 초기화 안 됨 → 속도 우선
- calloc()은 초기화되어 안전 → 안정성 우선
실제로 성능보다 안정성이 중요한 상황에서는 calloc()을 쓰는 것이 더 나은 선택일 수 있다.