Krafton Jungle/2. Keywords

[WEEK05] 동적 메모리 할당

munsik22 2025. 4. 11. 16:35

💡 정적 메모리 할당 vs 동적 메모리 할당

  • 정적 메모리 할당 (Static Memory Allocation)
    • 컴파일 타임에 메모리 크기와 위치가 정해짐.
    • 예) int arr[10]; 처럼 크기가 고정된 배열 선언
  • 동적 메모리 할당 (Dynamic Memory Allocation)
    • 런타임에 메모리를 요청하고 해제하는 방식.
    • 프로그램 실행 중에 메모리를 유연하게 사용할 수 있음.

🛠️ 왜 동적 메모리가 필요할까?

  • 사용자 입력이나 외부 데이터에 따라 필요한 메모리 크기가 달라질 수 있기 때문.
  • 메모리를 유동적으로 쓰면 낭비를 줄일 수 있음.
  • 구조체나 연결 리스트, 트리 같은 동적 자료구조를 만들 때 필수적임

📌 C 언어에서 사용하는 함수들

함수 설명
malloc(size) 지정한 크기만큼 메모리 할당, 초기화 X
calloc(n, size) n개의 요소를 size 크기로 할당, 0으로 초기화
realloc(ptr, new_size) 기존 메모리 블록 크기 변경
free(ptr) 할당된 메모리 해제
int* ptr = (int*)malloc(sizeof(int) * 5);  // int 5개 공간 동적 할당
if (ptr == NULL) {
    printf("메모리 할당 실패\n");
}
...
free(ptr);  // 꼭 할당 해제해줘야 함!

⚠️ 주의할 점

  • 할당한 메모리는 반드시 free()로 해제해야 한다 : 안 하면 메모리 누수(memory leak) 발생
  • 해제 후 포인터는 NULL로 초기화하는 습관 가지기
  • malloc 등의 결과는 널 포인터 체크 필수!

💻 예시: 사용자 입력 크기에 따라 배열 생성

#include <stdio.h>
#include <stdlib.h>  // malloc, free

int main() {
    int n;
    printf("몇 개의 정수를 입력하시겠습니까? ");
    scanf("%d", &n);

    // 정수 n개를 저장할 수 있는 동적 배열 할당
    int* numbers = (int*)malloc(n * sizeof(int));
    if (numbers == NULL) {
        printf("메모리 할당 실패\n");
        return 1;
    }

    // 사용자 입력 받기
    for (int i = 0; i < n; i++) {
        printf("숫자 %d 입력: ", i + 1);
        scanf("%d", &numbers[i]);
    }

    // 평균 계산
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += numbers[i];
    }
    double average = (double)sum / n;
    printf("평균: %.2f\n", average);

    // 동적 메모리 해제
    free(numbers);
    numbers = NULL;  // 포인터 초기화 (안전하게)

    return 0;
}

🔍 메모리 누수를 자동으로 체크해주는 툴

메모리 누수는 동적 메모리를 malloc() 등으로 할당한 후 free()를 호출하지 않아 사용하지 않는 메모리가 해제되지 않고 남아있는 상황을 말한다. 이를 자동으로 찾아주는 도구들이 있다.

✅ 대표적인 툴들

툴 이름 설명
Valgrind 가장 널리 쓰이는 메모리 디버깅 도구. 메모리 누수, 잘못된 접근, 초기화되지 않은 메모리 사용 등을 잡아줌.
AddressSanitizer (ASan) GCC/Clang에서 제공하는 컴파일 옵션. 빠르고 가볍게 메모리 문제를 탐지 가능.
Dr. Memory Windows에서 사용할 수 있는 Valgrind와 유사한 도구.
Visual Studio CRT Debugging Windows 개발환경에서는 Visual Studio의 디버깅 기능을 통해 메모리 누수를 추적 가능.

💡 Valgrind 사용 예시

valgrind --leak-check=full ./my_program

출력 결과로 어느 위치에서 메모리 누수가 발생했는지 알려준다고 한다.

🛡️ 동적 메모리를 더 안전하게 쓰는 방법

C 언어처럼 메모리를 수동으로 관리하는 언어에서는 실수를 방지하기 위한 몇 가지 패턴이나 팁이 있다.

1. 할당 후 NULL 체크하기

int* ptr = malloc(sizeof(int) * 10);
if (ptr == NULL) {
    // 에러 처리
}

2. 사용 후 반드시 free()

할당과 해제를 세트로 생각해야 한다.

free(ptr);
ptr = NULL;  // dangling pointer 방지

3. 구조체 기반의 메모리 관리

구조체에 할당 정보를 함께 넣어서 하나의 단위로 관리하는 것도 안전한 방법이다.

4. RAII (C++)

C++에서는 RAIIResource Acquisition Is Initialization 패턴을 쓰면 객체의 생성과 소멸 시 메모리를 자동으로 관리할 수 있다. (예: std::vector, std::unique_ptr, std::shared_ptr 사용)

5. 스마트 포인터 (C++)

std::unique_ptr<int> ptr(new int);
// 자동 해제됨

6. 라이브러리 사용

  • GLib (for C): 안전한 메모리 관리 함수 제공
  • Boehm GC: C에서도 사용할 수 있는 가비지 컬렉터 라이브러리

int *ptr; 로 포인터를 선언한 이후에도 ptr은 아직은 아무것도 가리키지 않는다. 여기서 우리는 둘 중에 하나를 선택할 수 있다.

  • 이미 존재하는 무언가를 가리키게 만들기
  • 포인터가 가리킬 새로운 무언가를 위한 메모리 공간을 할당하기
int *ptr, var, var2;
var = 5;
ptr = &var;
var2 = *ptr;

위의 코드는 첫 번째 경우에 대한 예시이다. 여기서 varvar2는 그것들을 위해 암묵적으로 할당된 공간을 가지고 있다.

 

포인팅할 새로운 무언가를 위한 공간을 할당하기 위해서는 malloc 함수를 사용해야 한다. (보통 typecast와 sizeof와 함께 쓰인다.)

ptr = (int *) malloc (sizeof(int));

이제 ptr은 메모리 내에서 크기가 sizeof(int)) 바이트인 어떤 공간을 가리킨다.

(int *)는 단순히 컴파일러에게 어떤 자료형(여기서는 int)이 이 공간에 들어갈 지 알려준다. (typecast라고 부른다.)

 

malloc이 호출되면, 그 메모리 장소는 어떤 것이든지 담을 것이기 때문에 그것의 값을 지정하기 전까지는 사용하면 안 된다.

 

동적으로 공간을 할당한 이후에, 우리는 그것을 동적으로 해방(free) 시켜야한다.

free (ptr);

이 커맨드는 clean up할 때 사용하면 된다.


  • Dynamic memory allocation
    • Obtain and release memory during execution
  • malloc
    • Takes number of bytes to allocate
      • Use sizeof to determine the size of an object
    • Returns pointer of type void *
      • A void * pointer may be assigned to any pointer
      • If no memory available, returns NULL
    • Example
      • newPtr = malloc(sizeof(struct node));
  • free
    • Deallocates memory allocated by malloc
    • Takes a pointer as an argument
    • free(newPtr);

[참고자료]

 

[C언어] 동적 메모리 할당 개념 잡기

동적 메모리 할당이란 컴퓨터 프로그래밍에서 실행 중(런타임)에 사용할 메모리 공간을 할당하는 것을 의미한다.프로그램이 실행되기 위해서는 메모리가 필요하다. 컴파일러는 컴파일 시점에

velog.io