
malloc은 필요한 바이트 수를 받아 메모리를 확보하는 함수이고, new 표현식은 타입에 맞게 저장 공간을 확보하고 초기화까지 수행합니다. 먼저 외울 것은 속도 차이가 아니라 할당과 해제의 짝입니다.
C 예제와 C++ 예제를 구분해 설명합니다. 일반적인 C++ 응용 코드라면 크기가 변하는 목록은 std::vector, 단독으로 소유할 동적 객체는 std::unique_ptr를 먼저 검토하면 해제 누락을 줄일 수 있습니다.

▲ 할당한 방식에 맞춰 해제합니다. 주소를 가리키는 포인터 타입만으로 해제 방식을 바꿀 수는 없습니다.
malloc과 new, 무엇이 다른가요?
| 비교 기준 | malloc | 일반적인 new 표현식 |
|---|---|---|
| 요청 단위 | 바이트 수: sizeof로 계산 | 타입과 원소 개수 |
| 초기화 | 확보한 메모리의 값을 초기화하지 않음 | 표현식에 따라 초기화. 클래스는 생성 과정 수행 |
| 실패 처리 | NULL 반환 확인 | 일반적인 throwing new는 std::bad_alloc |
| 해제 | free(p) | 단일 객체 delete p / 배열 delete[] p |
C에서는 <stdlib.h>, C++에서 std::malloc을 쓴다면 <cstdlib>를 포함합니다. 기본 new 문법 자체와 std::nothrow·std::bad_alloc 같은 라이브러리 이름은 구분해야 합니다. 후자는 <new>가 필요합니다.
C: 할당 실패를 확인하고 값을 넣습니다
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof *p);
if (p == NULL) return 1;
*p = 42;
printf("%d\n", *p);
free(p);
return 0;
}
이 예제는 42를 출력합니다. C에서는 malloc의 void* 반환값을 int*로 받을 때 캐스트가 필수는 아닙니다. sizeof *p를 쓰면 int가 항상 4바이트라고 가정할 필요도 없습니다. sizeof의 이 사용에서는 초기화되지 않은 p를 실제로 역참조하지 않습니다.
C++: new int(3)은 배열 세 칸이 아닙니다
#include <iostream>
#include <memory>
#include <vector>
int main() {
int *one = new int(3); // 값이 3인 int 한 개
int *many = new int[3]{}; // 0으로 초기화된 int 세 개
std::cout << *one << " " << many[0] << "\n";
delete one;
delete[] many;
auto owned = std::make_unique<int>(42); // C++14 이상
std::vector<int> values(3, 0);
values.push_back(7);
std::cout << *owned << " " << values.size() << "\n";
}
앞부분 출력은 3 0, 뒷부분은 42 4입니다. new int(3)은 정수 한 개를 3으로 초기화합니다. new int[3]{}는 정수 세 개를 만들고 0으로 초기화합니다. 중괄호가 없는 new int[3]의 원소를 값을 대입하기 전에 읽으면 안 됩니다.
원래 글의 new int(size) 다음에 배열 해제를 소개하던 예제는 서로 다른 할당 방식이 섞여 보일 수 있어 분리했습니다. delete와 delete[]를 바꿔 쓰거나, new의 결과를 free로 해제하는 코드는 잘못된 조합입니다.
크기를 늘리려면 realloc과 vector를 구분합니다
C에서 realloc을 사용할 때는 반환값을 임시 포인터로 받아 실패 여부를 확인합니다. 원래 포인터에 곧바로 덮어쓰면 실패 시 기존 블록의 주소를 잃을 수 있습니다. 여기서는 요청 크기가 0보다 큰 경우를 전제로 합니다.
int *next = realloc(p, new_count * sizeof *p);
if (next != NULL) {
p = next;
} else {
/* 기존 p는 그대로 유효합니다. 오류를 처리합니다. */
}
위 조각은 p가 malloc 계열로 확보한 블록이고, new_count와 곱셈 크기를 이미 검사했다고 가정합니다. new로 만든 객체에 realloc을 적용하지 않습니다. C++에서 목록의 크기가 변하면 vector의 resize·push_back을 사용해 객체의 이동과 수명 관리를 맡기는 쪽을 먼저 고려합니다.
해제 누락을 줄이는 선택 기준
| 상황 | 먼저 검토할 선택 |
|---|---|
| 지역에서만 필요한 객체 | 일반 지역 변수 |
| 크기가 바뀌는 연속된 목록 | std::vector |
| 한 곳이 소유하는 동적 객체 | std::make_unique |
| C API가 반환한 메모리 | 그 API가 지정한 해제 함수 |
unique_ptr와 vector는 소유 객체가 소멸할 때 관리하던 자원을 정리합니다. 직접 delete를 여러 반환 경로에 나눠 넣는 부담을 줄여 줍니다. 외부 라이브러리의 버퍼는 무조건 free가 아니라 해당 라이브러리의 계약을 확인해야 합니다.
자주 헷갈리는 두 가지
new를 쓰면 항상 0으로 채워지나요? 아닙니다. int 같은 스칼라의 초기화는 new int, new int(), new int(3)이 서로 다릅니다. 원하는 초기값을 코드에 명시하세요.
malloc과 new 중 어느 것이 더 빠른가요? 이 글에서는 속도를 측정하지 않았습니다. 객체 생성 비용, 할당 크기, 메모리 할당기와 사용 패턴이 달라 단순 순위로 판단하지 않습니다.
공식 자료와 확인일
내용 확인: 2026-09-30. 예제와 설명 도식은 이 글을 위해 새로 작성했습니다.
- Microsoft Learn: malloc
- Microsoft Learn: realloc
- Microsoft Learn: new와 delete
- C++ Core Guidelines: 자원 관리
기존 2016년 글의 참고 링크: 원래 글에 표시했던 출처. 이번 개정 내용과 예제는 위 공식 문서 및 가이드에 맞춰 다시 작성했습니다.