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

  • malloccalloc은 할당할 때만 사용하고,
  • 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()을 쓰는 것이 더 나은 선택일 수 있다.