10-3.동적 메모리 할당

10-3-가.할당의 필요성

프로그램이 실행되기 위해서는 메모리가 필요하다. 실행 파일 자체가 메모리에 로드(Load)되어야 실행될 수 있음은 물론이고 프로그램이 작업을 위해 선언하는 변수들도 모두 메모리에 할당된다. 다음과 같은 변수들을 사용한다고 해 보자.

 

int Score;

double Rate;

 

컴파일러는 이 두 변수들이 값을 제대로 보관할 수 있도록 변수의 타입에 맞는 크기만큼 메모리를 할당할 것이다. Score변수는 정수형이므로 4바이트가 할당되고 Rate는 실수형이므로 8바이트가 할당된다. 이런 식으로 프로그램을 작성할 때 미리 메모리 필요량을 알려주는 할당을 정적 할당(Static Allocation)이라고 한다. 정수형이나 실수형은 아주 작으며 이런 변수들 때문에 정적 할당이 실패하는 경우는 극히 드물다.

동적 할당(Dynamic Allocation)이란 프로그램을 작성할 때(Compile Time 또는 Design Time) 메모리 필요량을 지정하는 정적 할당과는 달리 실행중에(Run Time) 필요한만큼 메모리를 할당하는 기법이다. 어떤 경우에 동적 할당이 필요한지 보자. 일반적으로 응용 프로그램은 자신이 필요한만큼 메모리를 자유롭게 사용할 수 있다. 예를 들어 학생 50명의 성적을 처리하고 싶다면 다음 배열을 선언하여 정적 할당한다.

 

int arScore[50];

 

이렇게 선언하면 50명분의 성적을 저장할 수 있는 메모리 공간이 확보될 것이다. 만약 학생 수가 1000명이라면 첨자를 1000으로 늘리면 되고 전교생의 성적을 다 처리하고 싶다면 첨자를 얼마든지 더 크게 늘릴 수 있다. 모자란 것은 문제가 되지만 조금 남는 것은 당장 큰 문제가 되지 않으므로 여유분까지 고려하여 필요한 최대 크기만큼 메모리를 정적 할당하면 된다.

그러나 메모리 필요량을 프로그램 작성시에 전혀 예측할 수 없는 경우가 있다. 이 성적 처리 프로그램이 특정한 학교를 위한 것이 아니라 임의의 학교에 대해서 사용할 수 있는 일반적인 응용 프로그램이라고 해 보자. 학생이 10명도 채 안되는 시골 학교가 있는 반면 수만명이나 되는 경우도 있을 것이다.

그렇다고 해서 배열 크기를 100만 정도로 충분하게 정적 할당하는 것은 메모리를 지나치게 낭비하게 되므로 좋지 않다. 문제의 핵심은 필요한 메모리양이 많다는 것이 아니라 얼마나 필요한지 미리 알 수 없다는 것이다. 이런 경우는 실행중에 필요한 메모리양을 판단해서 학생수만큼만 메모리를 할당해야 한다. 그렇다면 다음과 같이 코드를 작성할 수 있을까?

 

int stNum;

printf("학생수를 입력해 주세요. ");

scanf("%d",&stNum);

int arScore[stNum];

 

프로그램 시작 직후에 학생 수를 질문하여 stNum에 입력받았다. 그리고 입력된 stNum크기만큼 arScore 배열을 선언했다. 언뜻 보기에는 이 코드가 제대로 동작할 것 같지만 컴파일해 보면 에러가 발생할 것이다. 왜냐하면 앞장에서 이미 알아 봤다시피 배열 선언문의 크기값은 변수로 지정할 수 없고 반드시 상수로만 지정할 수 있기 때문이다. 위 코드는 C를 아는 사람들이 보기에 굉장히 황당한 코드이지만 또한 초보자들은 이 코드가 동작하지 않는다는 사실에 당황스러워 한다.

stNum이라는 변수값은 실행중에 사용자가 입력하는 값이기 때문에 컴파일할 때는 이 값이 얼마가 될지 알 수 없다. 컴파일러가 아무리 똑똑해도 미래의 일을 예측할 수는 없으며 따라서 정적 할당시는 필요한 배열 크기를 변수로 지정할 수 없는 것이다. 메모리 필요량을 프로그램 작성중에 결정할 수 없을 때는 정적 할당할 수 없으며 동적 할당을 사용해야 한다.

동적 할당이 필요한 또 다른 경우는 임시적인 메모리가 필요할 때이다. 예를 들어 텍스트 파일에서 특정 문자열이 있는지만 알고 싶다고 하자. 텍스트 파일을 검색하려면 일단 이 파일을 읽어야 하고 그러기 위해서는 텍스트 파일을 읽기 위한 버퍼가 필요하다. 이런 버퍼를 미리 정적 할당해 놓을 필요없이 파일 크기만큼만 동적 할당한 후 원하는 작업만 하고 해제하면 된다.

 

char *buf=동적할당(파일크기만큼)

buf에 파일 읽음

원하는 작업 - buf에 문자열이 있는지 조사

buf 해제

 

이 파일 크기가 최대 1M라고 할 때 char buf[1048576]과 같이 전역 버퍼를 정적 할당해 놓고 작업하는 것도 가능하기는 하다. 임시 기억 공간인 스택은 용량이 그다지 크지 않기 때문에 buf는 지역변수로 선언할 수 없으며 전역으로만 선언할 수 있다. 하지만 이렇게 하면 이 프로그램이 항상 1M를 더 쓰게 되므로 시스템의 전반적인 효율이 떨어지게 될 것이다. 뿐만 아니라 텍스트 파일의 크기가 1M 이하여야 한다는 규칙은 어디에도 존재하지 않는다. 동적 할당이란 필요할 때 필요한만큼만 메모리를 할당해 사용하고 다 쓰면 버리는 것이다.

동적 할당된 메모리는 이름이 없는 변수라고 할 수 있다. 독점적인 메모리 영역을 차지하고 있으므로 일단 값을 기억할 수 있지만 이름이 없으므로 오로지 포인터로만 접근할 수 있다. 그래서 malloc 함수가 리턴하는 포인터는 반드시 적절한 타입의 포인터 변수로 대입받아야 한다. 시작 번지를 잃어버리면 할당된 메모리를 쓸 수 없음은 물론이고 다 사용하고 난 후에 해제하지도 못한다.