본문 바로가기
Computer Science/C 언어

[C #14] 동적 메모리 할당 (Dynamic Memory Allocation)

by rnasterofmysea 2024. 12. 12.
반응형

동적 메모리 할당 (Dynamic Memory Allocation)


1. 동적 메모리 할당이란?

  • 동적 메모리 할당은 프로그램 실행 중에 메모리를 동적으로 요청하고 해제하는 기능입니다.
  • C 언어에서는 malloc, calloc, realloc, **free**를 사용하여 메모리를 관리합니다.
  • 장점:
    • 컴파일 타임이 아닌 런타임에 필요한 만큼 메모리를 할당 가능.
    • 프로그램의 유연성과 효율성 증가.
  • 단점:
    • 메모리 누수 가능성.
    • 프로그래머가 직접 메모리를 해제해야 함.

2. 동적 메모리 할당 함수

1) malloc (Memory Allocation)

  • 메모리를 초기화하지 않고 필요한 크기만큼 할당.
  • 반환값: 할당된 메모리의 시작 주소를 가리키는 포인터.

사용법:

void* malloc(size_t size);

예제:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = (int *)malloc(5 * sizeof(int)); // 정수 5개 크기의 메모리 할당

    if (p == NULL) { // 메모리 할당 실패 시
        printf("메모리 할당 실패\n");
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        p[i] = i + 1; // 메모리 초기화
    }

    for (int i = 0; i < 5; i++) {
        printf("%d ", p[i]); // 메모리 내용 출력
    }

    free(p); // 메모리 해제
    return 0;
}

출력 결과:

1 2 3 4 5

2) calloc (Contiguous Allocation)

  • 초기화된 메모리를 할당. 할당된 모든 바이트는 0으로 초기화됨.
  • 반환값: 할당된 메모리의 시작 주소를 가리키는 포인터.

사용법:

void* calloc(size_t num, size_t size);

예제:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = (int *)calloc(5, sizeof(int)); // 정수 5개 크기의 메모리 할당 및 초기화

    if (p == NULL) {
        printf("메모리 할당 실패\n");
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        printf("%d ", p[i]); // 초기값 확인 (0으로 초기화됨)
    }

    free(p); // 메모리 해제
    return 0;
}

출력 결과:

0 0 0 0 0

3) realloc (Reallocate)

  • 이미 할당된 메모리의 크기를 재조정.
  • 기존 데이터를 유지하며 메모리 크기를 늘리거나 줄임.

사용법:

void* realloc(void* ptr, size_t new_size);

예제:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = (int *)malloc(3 * sizeof(int)); // 정수 3개 크기의 메모리 할당

    if (p == NULL) {
        printf("메모리 할당 실패\n");
        return 1;
    }

    for (int i = 0; i < 3; i++) {
        p[i] = i + 1; // 초기화
    }

    // 메모리 크기 확장 (3개 -> 5개)
    p = (int *)realloc(p, 5 * sizeof(int));

    if (p == NULL) {
        printf("메모리 재할당 실패\n");
        return 1;
    }

    p[3] = 4;
    p[4] = 5;

    for (int i = 0; i < 5; i++) {
        printf("%d ", p[i]);
    }

    free(p); // 메모리 해제
    return 0;
}

출력 결과:

1 2 3 4 5

4) free (Free Memory)

  • 동적으로 할당된 메모리를 해제.
  • 메모리 해제 후에는 해당 포인터를 더 이상 사용할 수 없습니다.

사용법:

void free(void* ptr);

예제:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = (int *)malloc(5 * sizeof(int));

    if (p == NULL) {
        printf("메모리 할당 실패\n");
        return 1;
    }

    free(p); // 메모리 해제

    return 0;
}

3. 동적 메모리 할당 주요 특징

1) 메모리 누수 (Memory Leak)

  • free를 호출하지 않으면 메모리 누수가 발생.
  • 메모리 누수는 프로그램이 종료되어도 메모리가 해제되지 않는 문제를 야기.
#include <stdlib.h>

void memoryLeakExample() {
    int *p = (int *)malloc(100 * sizeof(int));
    // free(p); -> 메모리 해제 누락으로 누수 발생
}

2) NULL 포인터 확인

  • 메모리 할당이 실패하면 반환값이 NULL.
  • 항상 메모리 할당 후 NULL 여부를 확인.
int *p = (int *)malloc(100 * sizeof(int));
if (p == NULL) {
    printf("메모리 할당 실패\n");
    return;
}

3) 사용 후 초기화

  • free를 호출한 후 포인터를 NULL로 설정하는 것이 좋습니다.
free(p);
p = NULL; // 더 이상 유효하지 않은 메모리를 참조하지 않도록 설정

4. 동적 메모리 할당을 활용한 심화 예제

1) 배열 확장

  • 동적으로 크기가 변하는 배열 구현.
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int size = 3, new_size;
    int *arr = (int *)malloc(size * sizeof(int));

    for (int i = 0; i < size; i++) {
        arr[i] = i + 1; // 초기화
    }

    printf("기존 배열: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }

    // 배열 크기 확장
    new_size = 5;
    arr = (int *)realloc(arr, new_size * sizeof(int));
    for (int i = size; i < new_size; i++) {
        arr[i] = i + 1;
    }

    printf("\n확장된 배열: ");
    for (int i = 0; i < new_size; i++) {
        printf("%d ", arr[i]);
    }

    free(arr); // 메모리 해제
    return 0;
}

출력 결과:

기존 배열: 1 2 3
확장된 배열: 1 2 3 4 5

2) 2차원 배열 동적 할당

  • 동적으로 할당된 2차원 배열 구현.
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int rows = 3, cols = 4;
    int **matrix = (int **)malloc(rows * sizeof(int *));

    for (int i = 0; i < rows; i++) {
        matrix[i] = (int *)malloc(cols * sizeof(int));
    }

    // 값 초기화
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j + 1;
        }
    }

    // 값 출력
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    // 메모리 해제
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);

    return 0;
}

출력 결과:

1 2 3 4
5 6 7 8
9 10 11 12

5. 동적 메모리 할당의 장단점

장점

  1. 유연성: 런타임 시 필요한

메모리만 할당. 2. 효율성: 메모리 낭비 최소화. 3. 다차원 데이터 구조 구현: 배열, 연결 리스트 등.

단점

  1. 메모리 누수 가능성.
  2. 프로그래머가 관리해야 함: 메모리 해제를 잊으면 누수 발생.
  3. 속도 문제: 동적 메모리 할당은 정적 메모리보다 느림.
반응형