C 언어 malloc 함수 완전 해설 | 동적 메모리 할당의 기초부터 응용까지

1. 소개

C 언어로 프로그램을 작성하기 시작하면, 처음에는 배열 등을 사용해 메모리를 다루는 경우가 많을 것입니다. 하지만 프로그램이 복잡해짐에 따라, 더 유연하게 메모리를 관리하고 싶어지는 상황이 생깁니다. 그럴 때 활약하는 것이 “동적 메모리 할당”입니다. malloc은 그 대표적인 기능으로, 프로그램 실행 중에 필요한 메모리를 동적으로 할당할 수 있습니다. 비유하자면, malloc은 “주문 후에 만들어지는 요리”입니다. 미리 정해진 메모리(배열)는 “뷔페 형식의 요리”와 같은 것이라고 할 수 있겠죠. 당신이 먹고 싶은 양만큼을 malloc으로 “주문”하고, 먹고 나면 “접시를 치우는(free 함수로 메모리를 해제하는)” 것이 기본적인 흐름입니다. 그럼, 이 글에서는 이 malloc에 대해 자세히 살펴보겠습니다.

2. malloc이란?

malloc은 “memory allocation(메모리 할당)”의 약자로, C 언어에서 메모리를 동적으로 확보하기 위한 함수입니다. 프로그램 실행 중에 지정한 크기만큼의 메모리를 확보하고 그 시작 주소를 반환합니다. 이를 통해 프로그램 실행 중에 필요한 만큼의 메모리를 사용할 수 있으며, 고정 크기 배열로는 어려운 유연한 메모리 관리가 가능해집니다. 실제 코드에서는 다음과 같이 malloc을 사용합니다。
int *array = (int*)malloc(10 * sizeof(int));
이 예에서는 정수형 배열을 10개분 확보하고 있습니다. 여기서 중요한 점은 malloc이 확보한 메모리의 시작 주소를 반환하기 때문에, 그대로는 타입이 맞지 않을 수 있다는 점입니다. 따라서 필요한 타입으로 캐스팅하는 것이 일반적입니다. 위에서는 (int*)를 사용하여 정수형 포인터로 캐스팅하고 있습니다。

3. malloc의 기본적인 사용법

그럼, 좀 더 자세히 malloc의 사용법을 살펴보겠습니다. 먼저, malloc의 간단한 구문은 다음과 같습니다。
void* malloc(size_t size);
malloc 함수는, 인자로 확보하고자 하는 메모리의 크기(바이트 수)를 받습니다. 그리고 그 크기만큼의 메모리 영역을 확보하고, 성공하면 그 영역의 시작 주소를 반환합니다. 반환되는 것은 void*형, 즉 어떤 형에도 캐스트할 수 있는 범용 포인터입니다. 예를 들어, 다음과 같이 사용합니다。
int *array = (int*)malloc(10 * sizeof(int));
여기서 sizeof(int)는 확보할 메모리의 크기를 구하기 위해 사용됩니다. 이렇게 하면, 다른 환경에서도 올바른 크기의 메모리를 확보할 수 있습니다. 확보한 메모리를 사용한 후에는 반드시 free 함수로 해제하는 것이 중요합니다. 해제하지 않으면 메모리 누수라는 문제가 발생합니다。

4. free()로 메모리를 해제하는 중요성

메모리의 동적 할당이 편리한 것은 틀림없지만, 한 가지 주의점이 있습니다. 그것은 할당한 메모리를 잊지 말고 해제해야 한다는 것입니다. 이를 소홀히 하면 메모리 누수가 발생하고, 프로그램이 대량의 메모리를 낭비하게 됩니다. malloc으로 할당한 메모리는 아래와 같이 free()로 해제합니다.
free(array);
해제되지 않은 메모리는 프로그램이 종료될 때까지 시스템 리소스로 남아 있으며, 장시간 동작하는 프로그램에서는 이것이 치명적인 문제가 될 수 있습니다. 말하자면, malloc으로 빌린 그릇을 free로 제대로 반환하지 않으면 주방이 그릇으로 가득 차는 것과 같습니다.

5. NULL를 확인하는 중요성

malloc 함수는 메모리 할당에 실패했을 경우 NULL을 반환합니다. 예를 들어, 할당하려는 메모리가 너무 커서 시스템이 할당할 수 없는 경우 등이 있습니다. malloc을 사용할 때는 반드시 이 NULL을 확인하여 메모리가 정상적으로 할당되었는지 확인하는 것이 안전한 프로그램 작성 방법입니다.
int *array = (int*)malloc(100000000 * sizeof(int));
if (array == NULL) {
    // 메모리 할당 실패 시 처리
    printf("메모리 할당 실패.
");
    return 1;
}
이와 같이 체크를 넣음으로써 메모리 할당 실패에 대한 오류 처리가 가능해집니다. 코드에 약간의 안전 조치를 넣음으로써 나중에 큰 문제를 예방할 수 있습니다.

6. malloccalloc의 차이

C 언어에는 malloc 외에도 메모리를 동적으로 할당하기 위한 함수가 있습니다. 그 중 하나가 calloc입니다. malloccalloc는 매우 유사하지만 몇 가지 중요한 차이점이 있습니다. malloc은 지정된 크기만큼의 메모리를 할당할 뿐이며, 그 내용은 초기화되지 않은 상태입니다. 반면 calloc은 메모리를 확보함과 동시에 할당된 메모리를 모두 0으로 초기화해 줍니다.

calloc 사용법

int *array = (int*)calloc(10, sizeof(int));
이 코드는 정수형 배열을 10개 확보하고 각각의 요소를 0으로 초기화합니다. malloc와의 주요 차이점은 calloc이 인자로 “요소 개수”와 “요소 크기” 두 가지를 받는다는 점입니다. 이 구문이 편리한 이유는 배열처럼 여러 요소를 가진 데이터를 다룰 때 보다 명확하게 메모리를 확보할 수 있기 때문입니다. 어떤 것을 사용할지는 상황에 따라 다르지만, 초기화가 필요한 경우 calloc이 편리합니다. 반대로 초기화가 필요 없거나 성능을 중시하는 경우 malloc이 더 적합합니다.

7. 실용 예: malloc을 사용한 문자열의 동적 할당

여기서는 실제로 malloc을 사용한 문자열의 동적 메모리 확보를 살펴보겠습니다. C 언어에서 문자열을 다룰 때는 보통 고정 크기의 배열을 사용합니다. 그러나 문자열 길이가 실행 시에만 알 수 있거나, 동적으로 문자열을 조작하고 싶을 때는 malloc이 편리합니다.
char *str = (char*)malloc(50 * sizeof(char));
if (str == NULL) {
    printf("메모리 할당에 실패했습니다.
");
    return 1;
}
sprintf(str, "안녕, 세계!");
printf("%s
", str);
free(str);
이 코드에서는 50자 분량의 메모리를 동적으로 확보하고, 그 영역에 “Hello, World!”라는 문자열을 저장하고 있습니다. 사용 후에는 free 함수로 메모리를 해제하는 것을 잊지 않도록 합시다. malloc을 사용하면 고정 크기 배열로는 불가능한 유연한 메모리 관리가 가능해집니다.

8. 구조체에 대한malloc 사용

다음으로, malloc을 사용하여 구조체의 메모리를 동적으로 할당하는 예를 살펴보겠습니다. 구조체는 여러 다른 타입의 데이터를 하나로 묶어 다룰 수 있는 강력한 데이터 타입이지만, 그 메모리 관리도 동적으로 수행할 수 있습니다.
typedef struct {
    int id;
    char *name;
} Person;

Person *p = (Person*)malloc(sizeof(Person));
if (p == NULL) {
    printf("메모리 할당 실패.\n");
    return 1;
}
p->name = (char*)malloc(50 * sizeof(char));
sprintf(p->name, "John Doe");
p->id = 1;

printf("ID: %d, 이름: %s\n", p->id, p->name);

free(p->name);
free(p);
이 코드에서는 Person이라는 구조체의 메모리를 동적으로 할당하고, 그 멤버 변수인 name에 대해서도 추가로 메모리를 동적으로 할당하고 있습니다. 이처럼 구조체의 각 멤버에 대해서도 필요에 따라 malloc을 사용함으로써 유연하게 메모리를 관리할 수 있습니다.

9. malloc 사용 시 흔히 발생하는 실수

malloc을 사용할 때 초보자가 저지르기 쉬운 실수에 대해서도 언급해 두겠습니다. 이러한 실수를 피함으로써 보다 안전하고 효율적인 프로그램을 작성할 수 있습니다.
  1. 메모리 해제 누락 동적으로 할당한 메모리를 free()로 해제하는 것을 잊으면 메모리 누수가 발생합니다. 이는 장시간 실행되는 프로그램에서 특히 문제가 될 수 있습니다. 아무리 복잡한 프로그램이라도 할당한 메모리는 반드시 해제하는 습관을 들이세요.
  2. NULL 체크 생략 메모리 할당이 실패했을 때 NULL이 반환된다는 것을 잊기 쉽습니다. 메모리 할당 직후에는 반드시 NULL 체크를 수행하고, 오류 처리를 구현합시다.
  3. 초기화되지 않은 메모리에 접근 malloc으로 할당한 메모리는 초기화되지 않은 상태입니다. 그대로 사용하려 하면 예기치 않은 동작을 일으킬 수 있습니다. 특히 초기화가 필요한 경우에는 calloc을 사용하는 것을 고려하세요.

10. 요약

malloc은 C 언어에서 강력한 도구로, 메모리를 동적으로 할당할 때 필수적입니다. 그러나 그 힘을 올바르게 사용하려면 확실한 이해와 적절한 메모리 관리가 필요합니다. 이번에 소개한 기본 사용법부터 구조체와 문자열에 대한 응용까지, 잘 실천에 옮겨봅시다. 다음에 프로그램을 작성할 때는 malloc으로 메모리를 할당하고, 사용이 끝났다면 반드시 반환하는 것을 잊지 마세요! FAQ
  1. malloc으로 메모리를 확보할 수 없을 때, 어떻게 해야 하나요? 메모리 할당이 실패하면 NULL이 반환되므로, 반드시 NULL 체크를 수행하고 적절히 오류 처리를 구현합시다.
  2. malloccalloc 중 어느 것을 사용해야 하나요? 초기화가 필요하지 않은 경우는 malloc, 메모리를 제로 초기화하고 싶을 때는 calloc이 적합합니다.
侍エンジニア塾