포인터와 메모리

C와 C++의 동적 메모리 관리

이 레슨에서는 실행할 때에야 크기를 알 수 있는 배열을 할당하고, 그 메모리를 안전하게 해제합니다.

소개

동적 메모리는 요소의 개수가 사용자의 입력에 따라 달라지는 경우처럼, 데이터의 크기나 수명을 컴파일할 때 정할 수 없을 때 씁니다. 이 메모리는 프로그램이 실행되는 동안 할당되며, 더 이상 필요하지 않으면 해제해야 합니다.

핵심 정리

  • 포인터는 아직 존재하는 올바른 타입의 객체만 가리키거나 null이어야 합니다.
  • 할당한 메모리 영역은 정확히 한 번만 해제해야 합니다.
  • 어떤 함수가 메모리를 소유(ownership)하는지, 어떤 데이터는 읽기만 하는지(const) 분명히 하세요.

C와 C++ 코드 예제

C
실행 →
main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t count = 5;
    int *values = calloc(count, sizeof *values);
    if (!values) return 1;
    for (size_t i = 0; i < count; ++i) values[i] = (int)(i * i);
    printf("last=%d\n", values[count - 1]);
    free(values);
    return 0;
}
실행 결과
last=16
C++
실행 →
main.cpp
#include <iostream>
#include <vector>

int main() {
    std::vector<int> values(5);
    for (std::size_t i = 0; i < values.size(); ++i) values[i] = static_cast<int>(i * i);
    std::cout << "last=" << values.back() << '\n';
}
실행 결과
last=16

C와 C++ 비교

C의 calloc은 정수 5개를 위한 메모리를 할당하고 0으로 초기화합니다. 프로그램은 할당이 실패했는지 확인해야 하고, 마지막에 free를 호출해 메모리를 해제해야 합니다. 그렇지 않으면 메모리 누수(memory leak)가 생깁니다. C++의 std::vector는 메모리를 스스로 관리하며, 예외가 발생하더라도 쓰지 않게 되면 자동으로 해제합니다.

C

메모리의 소유와 해제는 전적으로 프로그래머에게 달려 있습니다.

C++

컨테이너와 RAII가 객체의 수명이 끝날 때 자원을 자동으로 해제합니다.

연습 문제

두 버전을 모두 실행한 다음 코드를 바꿔 보면서 각 언어가 주는 안전성의 차이를 살펴보세요.

  • 널 포인터와 메모리 할당 실패 상황으로 테스트해 보세요.
  • AddressSanitizer와 UndefinedBehaviorSanitizer로 실행해 메모리 오류를 찾아보세요.
  • 어떤 포인터가 메모리를 소유하고 어떤 포인터는 읽기만 하는지 분명히 해 보세요.

챌린지로 연습하기

이 레슨에서 배운 내용으로 실제 문제를 풀어 보세요. 모든 챌린지는 브라우저에서 실행되며 즉시 테스트와 AI 리뷰를 받을 수 있습니다.

모든 챌린지