동적 메모리 할당이란 컴퓨터 프로그래밍에서 실행 중(런타임)에 사용할 메모리 공간을 할당하는 것을 의미한다.
프로그램이 실행되기 위해서는 메모리가 필요하다. 컴파일러는 컴파일 시점에 소스 코드를 읽고, 변수 타입들의 크기에 따라 메모리를 할당한다.
이처럼 프로그램이 실행되기 전, 컴파일 시점에 소스 코드를 읽고 메모리 공간을 확보하는 것을 정적 할당(static allocation) 이라고 한다.
그렇다면 동적 할당(dynamic allocation)이란 무엇일까? 컴파일 타임이 아닌 프로그램이 실행되는 중인 런타임에 필요한 만큼의 메모리 공간을 확보하는 것을 의미한다.
그렇다면 동적 할당이 필요하는 이유는 무엇일까? 그때 그때 필요할때마다 새로운 메모리 공간을 할당하는 것이 아니라, 컴파일 타임에 미리 넉넉한 메모리 공간을 확보해두면 되지 않을까? 라는 생각을 해볼 수 있다.
물론 가능은 하다. 그러나 메모리란 무한한 자원이 아니며, 한정 되어 있다. 만약 우리가 100000000 byte
사이즈의 메모리를 할당해두고 실제로는 10 byte
만 사용한다면, 남은 메모리 공간을 비효율적으로 사용하게 된다.
동적할당이 필요한 이유는 그때그때 필요한 만큼만 메모리 공간을 확보하고, 다 사용했다면 free
시켜 줌으로써 메모리 공간을 해제함으로서 한정된 메모리 공간을 효율적으로 사용할 수 있게 되는것이다.
이는 함수가 종료되거나 변수의 영역을 벗어나면 자동으로 메모리 해제가 이루어지는 스택과는 다르다. 그렇다면 동적 할당은 스택이 아닌 어디에서 메모리 할당과 해제가 일어날까?
힙(heap) 이란 C언어나 자바와 같은 프로그래밍 환경에서 원시 자료형(primitive type)이 아닌 보다 큰 크기의 데이터를 담고자 동적으로 할당하는 메모리 공간을 지칭한다.
정리해보자면 c언어에서 말하는 동적 할당(dynamic allocation)이란 힙(heap) 영역에 필요할 때마다 메모리 공간을 할당하고, 더 이상 필요하지 않을 경우 메모리를 해제해주는 과정을 의미한다. 이를 통해서 다음과 같은 장점을 취할 수 있다.
malloc
or calloc
)realloc
)free
)malloc
또는 calloc
을 호출하게 되면 힙 영역에 필요한 만큼의 메모리 공간을 확보하고 이후 반환 타입으로 해당 메모리 공간의 시작 위치를 포인터로 반환한다. 또한 할당된 메모리를 어떤 목적에 사용할지 함수에서 판단하기 어렵기 때문에, return 타입은 void *
형을 return 하며, 반환 받는 쪽에서 타입 캐스팅을 통해 사용해야한다.
malloc
과 calloc
의 차이각각의 선언 방법에 대한 차이는 아래와 같다
int *arr=(int *)malloc(10*sizeof(int));
int *arr=(int *)calloc(10,sizeof(int));
malloc
은 매개변수로 입력한 크기 만큼을 그대로 할당하는것이고, calloc
은 두번째 매개변수의 크기를 첫번째 매개변수 갯수 만큼을 할당해 달라는 식으로 요청한다. 위 두 명령어 모두 똑같은 크기의 공간을 확보한다.
두 명령어의 차이는 초기화 값에 그 차이가 있다. malloc
의 경우에는 메모리 공간만 할당하므로, 초기화되지 않은 메모리 공간에는 쓰레기 값들이 들어있다. 그러나 calloc
같은 경우에는 할당 후 해당 메모리 공간을 0으로 초기화한다.
따라서 성능 자체는 초기화를 시키지 않는 malloc
이 조금 더 낫다. 그러나 상황에 따라서 초기화가 필요할 경우는 calloc
을 선택하는것도 좋은 방법이 될 것 같다.
앞서서 확인한 메모리 할당 명령어인 malloc
또는 calloc
을 이용해서 메모리 공간을 할당하였고, 원하는 동작들을 모두 수행했다면 할당한 메모리 공간을 해제해주어야 한다.
처음 말했던 것처럼 우리의 메모리 공간은 한정적이며, 더 이상 사용되지 않을 메모리 공간이라면 해당 메모리 공간을 할당 해제 해줌으로써 다른 프로그램이 해당 공간을 재활용 할 수 있게 된다.
c언에서는 free
함수를 이용해 특정 주소의 메모리 공간을 할당 해제 해줄 수 있다.
메모리 누수란 동적으로 할당한 메모리가 할당 해제(free) 될 수 없는 상태가 된 것을 의미한다.
결국 위와 같은 상황들이 반복되면 동적으로 할당된 메모리가 해제되지 못하고 계속 남아있게 되기 때문에, 결국 시스템의 메모리가 부족해져 운영체제가 메모리 할당에 실패하여 프로그램을 종료시킨다.
메모리 누수의 간단한 예시는 아래와 같다.
int *a = malloc(5); // 5byte 메모리 공간 선언
int *b = malloc(10); // 10byte 메모리 공간 선언
b = a; // b가 a의 주소를 가리킴
free(a); // a할당 해제
free(b); // b도 a의 주소를 가리키므로, a할당 해제
위와 같은 상황이라면, b는 결국 힙 영역에 해제되지 않고 계속 남아있게 된다. 이러한 상황을 바로 메모리 누수(Memory leak)라고 한다.
이를 방지하기 위해서는 malloc
또는 calloc
을 사용했다면, 의식적으로 free
를 사용하는 습관을 들여야 하며, 동적 메모리할당을 한 포인터의 주소를 직접 바꾸는 코드는 작성하지 않는 것이 좋다.
이미 할당되어 있는 메모리 공간의 크기를 변경하여 재할당 하는 것을 의미한다.
재할당은 realloc
함수를 이용해 메모리 공간 재할당이 가능하다. realloc
의 함수 시그니처는 아래와 같다.
void *realloc(void *ptr, size_t size);
이를 해석해보면 ptr
위치에 해당하는 메모리 공간의 블록의 크기를 size
로 조절한다는 의미가 된다.
ptr == NULL
일 경우malloc
또는 calloc
을 이용해 새로운 메모리 공간을 할당하는것과 동일한 기능을 수행하게 된다.size == 0
일 경우ptr
위치의 메모리 블록 크기를 0으로 만든다는 의미이므로, 이는 곳 해당 메모리 공간을 할당 해제 한다는 의미와 같다. 따라서 이는 free
함수를 호출하는 것과 동일한 기능을 수행하게 된다.ptr != NULL
일 경우ptr
위치에 할당되어 있는 메모리가 있고, 이 메모리 블록의 크기를 size
로 변경하기 위한 동작을 수행한다.ptr
다음 블록이 이어서 위치한 상태라면, ptr
이 해당 위치에서 메모리 공간의 크기를 늘리는것은 어렵기 때문에, 어쩔 수 없이 기존 ptr
블록의 내용을 유지한 채로 확장된 메모리 블록을 새로운 주소에 할당받게 된다.8byte
크기만큼의 블록을 4byte
만큼으로 축소시키려고 한다면, 재할당하는 4byte
블록의 내용은 기존 8byte
블록의 4byte
까지의 내용으로 채워지게 된다.참고