malloc과 new 차이: 배열 할당·해제 실수와 C++ 선택 기준

작성자

카테고리:

개발 로봇들이 메모리 블록의 할당·반환과 버튼 실행 연결을 점검하는 만화풍 장면
AI로 제작한 만화풍 창작 캐릭터 삽화입니다. 공식 마스코트·실제 제품 화면·실험 또는 벤치마크 증거가 아닙니다.

malloc은 필요한 바이트 수를 받아 메모리를 확보하는 함수이고, new 표현식은 타입에 맞게 저장 공간을 확보하고 초기화까지 수행합니다. 먼저 외울 것은 속도 차이가 아니라 할당과 해제의 짝입니다.

C 예제와 C++ 예제를 구분해 설명합니다. 일반적인 C++ 응용 코드라면 크기가 변하는 목록은 std::vector, 단독으로 소유할 동적 객체는 std::unique_ptr를 먼저 검토하면 해제 누락을 줄일 수 있습니다.

malloc과 free, new와 delete, new 배열과 delete 배열의 세 가지 대응 관계

▲ 할당한 방식에 맞춰 해제합니다. 주소를 가리키는 포인터 타입만으로 해제 방식을 바꿀 수는 없습니다.

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. 예제와 설명 도식은 이 글을 위해 새로 작성했습니다.

기존 2016년 글의 참고 링크: 원래 글에 표시했던 출처. 이번 개정 내용과 예제는 위 공식 문서 및 가이드에 맞춰 다시 작성했습니다.