Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C 언어 동적 메모리 할당 완벽 가이드 – malloc, calloc, realloc, free 함수 총정리

C 프로그래밍 언어에서 동적 메모리 할당(Dynamic Memory Allocation)은 프로그램 실행 도중 필요한 만큼 메모리를 유연하게 확보하고 해제할 수 있는 기능입니다. 배열처럼 컴파일 시점에 크기를 고정하지 않고, 런타임에 상황에 맞게 메모리를 관리할 수 있어 효율적인 프로그램 작성에 필수적인 개념입니다.

C 언어는 메모리 할당과 관리를 위한 여러 표준 함수를 제공하며, 이 함수들은 <stdlib.h> 헤더 파일에 선언되어 있습니다.

동적 메모리 할당 주요 함수

함수설명
void *calloc(int num, int size);크기가 size바이트인 요소 num개로 이루어진 배열을 할당하고, 모든 바이트를 0으로 초기화합니다.
void free(void *address);address가 가리키는 메모리 블록을 해제합니다.
void *malloc(int num);num바이트의 메모리를 할당하며, 초기화를 수행하지 않습니다(기존 쓰레기 값이 남아 있을 수 있음).
void *realloc(void *address, int newsize);address가 가리키는 기존 메모리 블록을 newsize 크기로 다시 할당(확장 또는 축소)합니다.

동적으로 메모리 할당하기

프로그래밍을 하다 보면 저장할 데이터의 크기를 미리 알 수 있는 경우도 있습니다. 예를 들어 사람 이름을 저장할 때 최대 100자까지 들어간다고 가정하면, 다음과 같이 간단히 배열로 선언할 수 있습니다.

char name[100];

그러나 저장해야 할 문자열의 길이를 미리 알 수 없는 경우도 많습니다. 예를 들어 어떤 주제에 대한 상세한 설명을 저장한다고 생각해 봅시다. 이럴 때는 필요한 메모리 크기를 미리 정하지 않고 문자형 포인터만 선언한 뒤, 실제 필요한 시점에 요구 사항에 맞춰 메모리를 동적으로 할당하면 됩니다.

예제 코드

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

int main() {
   char name[100];
   char *description;

   strcpy(name, "Adam");

   /* 메모리를 동적으로 할당 */
   description = malloc( 200 * sizeof(char) );

   if( description == NULL ) {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   } else {
      strcpy( description, "Adam a DPS student in class 10th");
   }

   printf("Name = %s\n", name );
   printf("Description: %s\n", description );
}

실행 결과

Name = Adam
Description: Adam a DPS student in class 10th

위 코드에서 malloc() 대신 calloc()을 사용해도 동일한 결과를 얻을 수 있습니다. 호출 방식만 아래와 같이 바꾸면 됩니다.

calloc(200, sizeof(char));

두 함수의 차이는 초기화 여부입니다. malloc()은 할당된 메모리를 초기화하지 않지만, calloc()은 모든 바이트를 0으로 채워 줍니다.

이처럼 동적 할당을 활용하면 메모리 크기를 개발자가 직접 제어할 수 있습니다. 배열은 한 번 크기를 정하면 변경할 수 없지만, 동적 메모리는 원하는 크기를 자유롭게 지정할 수 있다는 점이 큰 장점입니다.

메모리 크기 조절(realloc)과 해제(free)

프로그램이 종료되면 운영체제가 해당 프로그램이 사용한 모든 메모리를 자동으로 회수합니다. 그러나 좋은 프로그래밍 습관으로서, 더 이상 메모리가 필요하지 않을 때는 free() 함수를 호출해 직접 해제하는 것이 좋습니다. 이를 소홀히 하면 장시간 실행되는 프로그램에서 메모리 누수(memory leak)가 발생할 수 있습니다.

또한 realloc() 함수를 사용하면 이미 할당된 메모리 블록의 크기를 늘리거나 줄일 수 있습니다. 앞선 예제에 realloc()과 free()를 적용해 다시 살펴보겠습니다.

예제 코드

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

int main() {
   char name[100];
   char *description;

   strcpy(name, "Adam");

   /* 메모리를 동적으로 할당 */
   description = malloc( 30 * sizeof(char) );

   if( description == NULL ) {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   } else {
      strcpy( description, "Adam a DPS student.");
   }

   /* 더 큰 설명을 저장하고 싶다고 가정 */
   description = realloc( description, 100 * sizeof(char) );

   if( description == NULL ) {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   } else {
      strcat( description, "He is in class 10th");
   }

   printf("Name = %s\n", name );
   printf("Description: %s\n", description );

   /* free() 함수로 메모리 해제 */
   free(description);
}

실행 결과

Name = Adam
Description: Adam a DPS student.He is in class 10th

위 예제에서 realloc()으로 추가 메모리를 확보하지 않은 상태에서 strcat()을 호출하면, description 버퍼에 남은 공간이 부족해 버uffer 오버플로우 오류가 발생할 수 있습니다. 따라서 문자열을 이어 붙이기 전에 반드시 충분한 메모리를 먼저 확보해야 합니다.

참고로, 안전성을 높이려면 realloc()의 반환값을 임시 포인터에 받아 NULL인지 확인한 후 원래 포인터에 대입하는 것이 좋습니다. 실패 시 반환값을 곧바로 원래 포인터에 대입하면 기존 메모리의 주소를 잃어버려 해제할 수 없게 되기 때문입니다.

마무리

정리하면, C 언어의 동적 메모리 관리는 malloc()/calloc()으로 메모리를 확보하고, realloc()으로 크기를 조절하며, free()로 해제하는 흐름으로 이루어집니다. 네 함수를 올바르게 활용하면 실행 시점의 요구 사항에 맞춰 메모리를 효율적으로 사용하는 견고한 프로그램을 작성할 수 있습니다.