작은숲:위키노트/초보자를 위한 C 언어 강의/동적 메모리 할당
C 언어를 공부한 사람은 동적 메모리 할당(Dynamic memory allocation)이라는 말을 들어봤을 것이다. 왜 동적 메모리 할당을 사용해야 할까? 이 대답에 자신있게 대답하실 수 있는 사람은 C 언어에 대해 어느 정도 이해했다고 생각해도 좋을 듯 싶다. 아래에 나오는 내용은 "절대적인 것은 아니다." 이 내용은 개인적인 의견이라는 것을 먼저 밝힌다. 동적 메모리 할당은 데이터를 저장하기 위해 사용할 메모리를 할당해준다는 점에서 배열과 유사하다. 하지만, 배열의 경우 그 크기를 미리 지정해줘야 한다. 예를 들어,
int array[100];이와 같이 말이다.
이 array라는 배열에 저장시킬 데이터의 양이 유동적이라면 배열의 크기를 결정하는데 상당히 곤란할 수 있다. 예를 들어, 어떤 경우에는 10개의 데이터만 저장시키면 되는데 어떤 경우에는 10000개의 데이터를 저장해야 한다면 배열의 크기를 어떻게 잡아야 할까? 데이터를 저장 못시키는 경우를 없애기 위해 우리는 눈물을 머금고 배열의 크기를 10000으로 잡아줘야 한다. 하지만, 만약 10개의 데이터만 저장을 시킨다면 나머지 9980개의 공간은 낭비하게 된다.
아주 극단적인 예를 들었습니다만, 이와 같이 데이터의 양이 상당히 유동적인 경우, 배열을 사용하면 낭비되는 공간이 많이 생길 수 있다. 이런 낭비를 동적 메모리 할당을 이용한다면 줄여줄 수 있다.
또, 일부 컴파일러에서는 자동 변수를 위한 메모리의 공간이 상당히 제한적이다. 이런 경우에는 굉장히 큰 배열을 자동 변수로 사용할 수 없다. 정적으로 선언해주거나 혹은 외부 변수로 선언해줘야 한다.
동적 메모리 할당이라는 게 뭘까? 말 그대로, 동적으로 메모리를 할당해준다는 것이다. 필요할 때 필요한 만큼 메모리를 할당해준다는 말이다. 배열처럼 배열 선언해줄 때 크기를 지정해줄 것이 아니라, 나중에 필요하면 메모리를 할당해서 사용할 수 있도록 하는 것이 바로 동적 메모리 할당이라고 말할 수 있다. 동적 메모리 할당을 유용하게 사용한다면 상당히 고급스러운 데이터 구조를 표현할 수 있다.
C 언어에서 동적 메모리 할당을 하기 위해서 malloc() 함수를 사용한다. 이 malloc() 함수는 stdlib.h에 선언되어 있다.
malloc() 함수의 원형은 다음과 같다.
void *malloc(size_t size);malloc() 함수의 매개변수는 size이다. 이 크기는 얼마만큼의 크기로 메모리를 할당할 것인지를 결정한다. 이 함수가 호출이 되면, 일단 메모리 상에서 지정한 크기만큼의 빈 공간이 있다면 그 공간을 할당해준다. 그 할당한 메모리의 주소를 void 형 포인터로 반환해준다.
void 형 포인터는 형변환 연산자를 사용해서 어떤 데이터형의 포인터에나 대입을 시킬 수 있다.
이렇게 메모리를 할당하고, 그 할당된 메모리의 주소를 알고 있다면 우리는 그 공간을 사용할 수 있다. 이건 아주 간단한 일이죠. C 언어에서 포인터를 이해하고 있다면 쉽게 알 수 있을 것이다.
예를 들어, 하나의 정수형 값을 저장하기 위해 동적 메모리 할당을 해서 사용해보도록 하자.
...
int *ptr;
ptr = (int *) malloc(sizeof(int));
if (ptr == NULL)
{ printf("Could not allocate memory!\n");
exit(1);
}*ptr = 10;
printf("*ptr = %d\n", *ptr);
...이처럼 정수형 크기만큼 메모리를 할당해서 그 공간에 어떤 수를 저장시키고 사용할 수 있다. 그리고, 한가지 주의할 사항이 있다. 위에서 보면, 메모리 할당을 한 다음에 ptr이 NULL과 같은지 검사하고 있다. ptr이 NULL인 경우는, 즉 malloc() 함수가 리턴하는 값이 NULL인 경우는 오직 한 경우 뿐이다. 메모리 할당에 실패했을 경우에 malloc() 함수는 NULL을 리턴하게 됩니다. 따라서, 이렇게 동적 메모리 할당을 해서 사용할 때는 반드시 malloc() 함수의 리턴값을 검사해줘야 한다. 제대로 메모리가 할당되지 못 했을 때는 프로그램의 수행을 중단하고 적절한 조취를 취해야 한다.
다음 예를 보도록 하자.
...
int i;
int *ptr;
ptr = (int *) malloc(sizeof(int) * 10);
if (ptr == NULL)
{ printf("Could not allocate memory!\n");
exit(1);
}for (i = 0; i < 10; i++)
ptr[i] = i * 3;
for (i = 0; i < 10; i++)
printf("ptr[%d] = %d\n", i, *(ptr + i));
...동적 메모리 할당을 통해 정수형 값을 10개 저장시킬 수 있는 공간을 만들었다. 이렇게 할당한 메모리는 배열과 다를 바가 없다. 연속된 공간 내에 할당된 10개의 정수형 공간은 크기를 10으로 한 정수형 배열과 같다. 이렇게 할당함으로서 배열과 같이 사용할 수 있다.
실제 동적 메모리 할당을 이렇게 사용하는 경우는 거의 없을 것이다. 동적 메모리 할당을 사용한다는 것은 메모리 사용을 효율적으로 하자는 것인데, 이것은 배열과 다를 바가 없는 것이므로 복잡하게 동적 메모리 할당을 해준 의미가 없어진다. 물론 배열의 크기가 아주 크다면 의미는 있을 것이다. 프로그램에서 사용할 수 있는 자동변수를 위한 공간은 제한적이기 때문에 아주 큰 배열을 할당하지 못할 수도 있다. 이런 경우라면 동적 메모리 할당을 통해 자동변수를 위한 공간이 아닌 다른 공간을 사용함으로서 이 문제를 해결할 수 있다. 하지만, 이런 경우는 극히 드물 것이고, 또 이런 식으로 프로그래밍을 한다면 그 프로그램은 상당히 비효율적인 프로그램이라고 할 수 있다.
상당히 많은 양의 데이터를 다뤄야하는 프로그램에서는 메모리 관리하는 것도 상당히 중요한 문제이다. 이런 경우에는 일반적으로 배열을 사용하지 않고, 좀더 효율적으로 메모리를 사용할 수 있는 동적 메모리 할당을 사용한다. 그렇다고 무작정 "동적 메모리를 사용해라!"라고 한다면 막막하다.
열었으면 닫아라!
이 말은 프로그래밍을 하신다면 절대 잊어서는 안되는 말이다. 어느 프로그래밍 언어를 사용하던 반드시 지켜지는 원칙 중의 하나이다. 아주 간단하지만, 잊어버리기 쉬운 것 중의 하나이다.
예를 들어, 블럭을 시작했다면 반드시 블럭을 닫아야 한다. 또, 파일 입출력을 하기 위해 파일을 열었다면 반드시 닫아야 한다.
이것은 메모리 할당에도 그대로 적용된다. 일반 변수나 배열과는 다르게 동적으로 할당된 메모리 공간은 우리가 닫아줘야 한다. 열어놓은 사람이 닫아야 하는 건 당연한 것이다. '프로그램이 알아서 닫아주겠지'라는 안일한 생각은 하지 말아야 한다. 이건 아주 위험한 생각이며, 잘못하다가는 사용자의 시스템을 멈추게 만드는 원인이 될 수도 있다.
메모리를 계속 할당만 하고, 사용하고 나서 해제해주지 않는다면, 이런 공간들이 쌓이고 쌓여서 나중에는 다른 프로그램이나 시스템에서 사용할 메모리를 하나도 남겨두지 않을 수도 있다. 따라서, 동적 메모리 할당을 사용했다면, 그 공간을 다 사용한 다음에는 반드시 해제해줘야 한다. 할당된 공간을 해제하는 것은 free() 함수를 사용한다. 다음 예를 보도록 하자.
...
int *ptr;
ptr = (int *) malloc(sizeof(int));
if (ptr == NULL)
{ printf("Could not allocate memory!\n");
exit(1);
}*ptr = 10;
printf("*ptr = %d\n", *ptr);
...
free(ptr);이렇게 할당된 메모리를 사용한 후에는 free() 함수의 매개변수로 할당된 공간의 시작 주소가 들어있는 포인터를 넘겨줌으로서 그 공간을 해제할 수 있다.
이처럼 C 언어에서는 자동변수의 영역만으로는 저장할 수 없는 큰 데이터를 저장하기 위해 동적 메모리 할당을 제공한다. 하지만 이렇게 할당된 메모리 공간은 자동변수와는 달리 프로그램에서 관리하지 않기 때문에 프로그래머가 항상 신경 써야 한다. 앞에서도 말했지만 할당된 메모리 공간은 다 쓴 후 반드시 해제해야 한다. 그렇지 않으면 메모리 누수가 계속 생겨, 이게 누적되면 시스템에 악영향을 주게 된다. "열었으면 반드시 닫아야 한다"는 것을 항상 신경 쓰고, 프로그래밍 하는 동안은 버릇이 되도록 해야 한다.
#redirect 작은숲:위키노트/초보자를 위한 C 언어 강의/동적 메모리 할당