공부/C언어

[C언어] 동적 메모리 할당 완벽 정리 | C 프로그래밍

강갱갱 2025. 6. 14. 11:55

 

 

13장 동적 메모리 할당 완벽 정리 | C 프로그래밍

이번 장에서는 동적 메모리 할당에 대해 자세히 알아보겠습니다. C언어에서 메모리를 사용하는 방법은 크게 두 가지가 있습니다. 바로 정적 메모리 할당동적 메모리 할당입니다. 각 방법의 특징과 장단점, 그리고 실제 코드에서 어떻게 활용하는지 예제와 함께 자세히 살펴보겠습니다.

1. 정적 메모리 할당 (Static Allocation)

정적 메모리 할당은 프로그램이 실행되기 전에 메모리 크기가 미리 정해져서 할당되는 방식입니다. 예를 들어, 배열을 선언할 때 크기를 고정하여 선언하는 것이 정적 할당입니다.

int array[10];  // 10개의 int형 변수를 미리 할당

장점은 간단하고 빠르다는 점이지만, 메모리 크기가 고정되어 있어 실행 중에 크기를 변경할 수 없다는 단점이 있습니다.

2. 동적 메모리 할당 (Dynamic Allocation)

동적 메모리 할당은 프로그램이 실행되는 도중 필요한 크기만큼 메모리를 할당받는 방법입니다. 이는 <stdlib.h> 라이브러리의 함수를 사용하여 구현할 수 있습니다.

2-1. 주요 함수와 사용법

  • malloc()</> : 필요한 크기만큼 메모리를 할당받으며, 초기화는 하지 않음.
  • calloc()</> : 메모리를 할당받고 0으로 초기화함.
  • realloc()</> : 이미 할당된 메모리 크기를 변경함.
  • free()</> : 사용한 동적 메모리를 해제하여 시스템에 반환함.

2-2. malloc() 사용 예시

int* score;
score = (int*) malloc(10 * sizeof(int));  // int 10개 크기의 메모리 할당
if(score == NULL) {
    // 메모리 할당 실패 시 오류 처리
    printf("메모리 할당 실패\n");
    exit(1);
}

malloc()void* 타입을 반환하기 때문에, 필요한 타입으로 캐스팅해서 사용합니다. 또한 메모리 할당에 실패할 경우 NULL을 반환하므로 반드시 검사해야 합니다.

2-3. calloc() 예시

int* p;
p = (int*) calloc(5, sizeof(int));  // 5개의 int 크기 메모리를 0으로 초기화하여 할당

calloc()malloc()과 달리 할당한 메모리를 모두 0으로 초기화해 줍니다. 따라서 초기화된 배열이 필요할 때 유용합니다.

2-4. realloc() 예시

int* p;
p = (int*) malloc(5 * sizeof(int));  // 5개 int 크기 메모리 할당
p = (int*) realloc(p, 7 * sizeof(int));  // 기존 메모리 크기를 7개 크기로 확장

realloc()은 기존 메모리 블록 크기를 변경하며, 기존 데이터는 보존됩니다. 필요에 따라 메모리 크기를 동적으로 조절할 때 사용합니다.

3. 동적 메모리와 구조체 활용

동적 메모리는 단순 변수뿐만 아니라 struct 타입에도 사용할 수 있습니다. 구조체 배열을 동적으로 생성할 때는 다음과 같이 합니다.

struct Movie {
    char title[50];
    int year;
};

struct Movie* list;
int size = 10;

list = (struct Movie*) malloc(size * sizeof(struct Movie));
if(list == NULL) {
    // 오류 처리
}

4. 배열 vs 연결 리스트 (Linked List)

배열연결 리스트는 데이터를 저장하는 대표적인 자료구조입니다.

구분 배열 (Array) 연결 리스트 (Linked List)
메모리 할당 정적 (크기 고정) 동적 (필요한 만큼 할당)
접근 방식 인덱스로 빠른 랜덤 접근 가능 순차적 접근, 랜덤 접근 불가
삽입 및 삭제 비효율적, 크기 변경 어려움 중간 삽입/삭제 용이
복잡도 간단하고 빠름 구현 복잡, 포인터 필요

5. 연결 리스트의 구조와 예제

연결 리스트는 노드(Node)들이 포인터로 서로 연결된 구조입니다. 각 노드는 데이터 필드링크 필드를 포함합니다. C언어에서는 자기 참조 구조체(self-referential struct)를 사용해 구현합니다.

typedef struct NODE {
    int data;
    struct NODE* link;  // 다음 노드를 가리키는 포인터
} NODE;

5-1. 간단한 연결 리스트 생성

NODE* p1;
p1 = (NODE*) malloc(sizeof(NODE));
p1->data = 10;
p1->link = NULL;

NODE* p2;
p2 = (NODE*) malloc(sizeof(NODE));
p2->data = 20;
p2->link = NULL;

// p1이 p2를 가리키도록 연결
p1->link = p2;

이렇게 노드들을 동적으로 생성하고 연결하면 동적으로 크기가 변하는 리스트를 만들 수 있습니다.

5-2. 동적 메모리 해제

연결 리스트나 동적 메모리를 사용한 후에는 반드시 free() 함수를 사용하여 메모리를 해제해야 합니다.

free(p1);
free(p2);

하지만 만약 리스트가 여러 개의 노드로 이루어져 있다면, 노드 하나하나를 순차적으로 해제해야 합니다. 단순히 free(p1)만 하면 p2에 할당된 메모리는 해제되지 않으니 주의가 필요합니다.

Tip! 연결 리스트를 해제할 때는 다음과 같은 루프를 이용해 모든 노드를 안전하게 해제합니다.
NODE* temp;
while(p1 != NULL) {
    temp = p1;
    p1 = p1->link;
    free(temp);
}

마무리

동적 메모리 할당은 프로그램 실행 중 메모리를 유동적으로 사용할 수 있어 효율적입니다. 하지만 메모리 누수나 할당 실패에 주의하며 적절히 free() 해 주어야 합니다. 또한 연결 리스트 등 포인터를 활용한 자료구조 구현 시 동적 메모리 사용법이 매우 중요하니 꼭 이해하고 익혀 두세요!

반응형