[C/C++] 동적 메모리 할당 malloc(), calloc(), realloc(), free()
메모리에서 스택영역, 데이터영역, 힙영역 이런 말을 한번쯤은 들어 보셨을겁니다.
이 메모리들은 각각 특성이 있습니다. 일단 메모리 그림을 한번 보고 가시죠.
메모리가 이렇게 생겼구나.. 정도만 알고 가시면됩니다 ㅇㅅㅇ
정적메모리는 보통 스택영역, 데이터영역을 의미합니다. 메모리 크기는 컴파일할 때 결정되며 실행 도중에 해제되지 않고, 프로그램이 종료할 때 알아서 운영 체제가 회수합니다. C 언어에서 정적 할당된 메모리는 스택에 위치하기 때문에 할당 받을 수 있는 메모리에 제약을 받습니다.
말그대로 컴파일 시 결정된 크기만큼 그 딱그만큼만 쓸 수 있다는거죠. 실행중에 변경이 불가능합니다.
그와 반대로
동적메모리는 프로그램 실행 시간(런타임) 중에 프로그래머가 동적으로 메모리를 할당 할 수 있습니다. 한마디로 내맘대로 메모리를 지정하고 사용하고 반납할 수 있다는거죠. 힙 영역같은 동적 메모리가 요구됩니다.
동적메모리는 왜? 필요하고 어떻게 할당하고 접근할 수 있을까요?
동적메모리를 왜 쓸까? 보통은 큐, 스택, 연결리스트, 이진트리와 같은 자료구조를 표현할 때 사용됩니다 . 정적메모리 사용시 메모리가 할당된 만큼 사용하지 않아서 메모리 낭비가 일어나고, 선언된 메모리 공간보다 많은 메모리를 사용시에는 메모리 부족현상도 일어날 수 있죠.
그럼 동적 메모리 사용을 위한 함수들을 알아봅시다.
★ 먼저 동적 메모리 함수들을 이용하기 위해서 stdlib.h 을 선언해 줍시다.
malloc() 함수 |
void *malloc(size_t size)
먼저 malloc 함수 입니다. 메모리 사이즈를 입력받아 단순히 동적메모리를 생성하는 함수입니다.
입력인자가 size_t size 형태이며 size_t는 typedef로 재정의해서 만들어진 사용자 정의 자료형입니다. typedef에 관해서는 알고계시리라 믿겠습니다. size_t는 unsigned int , unsigned long 형 입니다.
리턴형태는 void*로 void형 포인터 입니다. 할당된 동적메모리의 void형 시작주소를 반환합니다.
void형 시작주소?? 이게 무슨소리지? 하시는분 계실거같아서 설명드립니다. malloc 함수는 단순히 메모리를 할당하고 해당주소를 반환합니다. malloc 가 스스로 어떤 자료형의 주소인지 결정하지 못합니다. 그래서 malloc는 단순히 void형 주소를 반환하고, 프로그래머가 필요한 자료형으로 알아서 형변환해서 사용합니다.
예를들어 int형으로 쓰고싶다하면 ...
int *ptr=NULL;
ptr = (int*)malloc(4); 이런식으로 가능합니다.
free() 함수 |
void free(void *ptr)
위에서 설명했듯이 정적메모리는 우리 컴퓨터가 착하게도 알아서 메모리를 회수해준다고 했습니다. 하지만 동적메모리는 사용자가 직접 메모리 해제도 해주어야 합니다. 할당만 자꾸 하다간 메모리가 남아나질 않겠죠??
해제는 매우 간단합니다. 메모리 해제함수 free를 선언하고 malloc 얘가 리턴해준 주소값을 인자로 넘겨주면 끝입니다. malloc 얘는 할당된 시작메모리의 주소를 리턴한다고 했죠? (calloc, realloc 도 마찬가지입니다)
하나 주의할점은 해제시 특정 동적메모리의 영역만을 해제할수 없고 유효하지 않은 포인터로 해제를 하게되면 런타임시 메모리 문제가 발생하게됩니다.
먼저 이 두 함수를 이용하여 간단하게 동적메모리 할당을 해봅시다.
#include<stdlib.h> #include<stdio.h> int main(){ int *ptr = NULL; ptr = (int*)malloc(4); // 동적메모리 4바이트 생성 *ptr = 1234; // 동적 메모리 사용 free(ptr); // 동적 메모리 해제 ptr = NULL; // 동적 메모리내 데이터 초기화 return 0; }
간단하게 생성, 사용 , 해제 순으로 진행했습니다. 쉽죠?
calloc 함수 |
void *calloc(size_t num, size_t size)
위의 malloc 함수를 이해하셨다면 calloc 함수는 껌입니다. 동작방식이 매우매우 비슷하거든요. 입력 인자들을 살펴보면 size_t num이 추가 되었는데 여기서 num은 동적메모리 생성개수를 의미합니다.
num을 5를주고 size에 10을 주게된다면 총 10바이트의 동적메모리가 5개 만들어져서 생성된 메모리의 크기는 총 50바이트가 되겠죠? 리턴값은 void형 동적메모리 시작주소로 malloc과 같습니다.
이것말고도 malloc 함수와 다른점이 하나 더 있습니다. 동적메모리 할당시 malloc 함수는 할당된 메모리를 초기화 시키지 않지만 calloc 함수는 자동으로 0으로 초기화 시켜줍니다.
calloc 도 마찬가지로 메모리를 생성후에 free 함수로 동적메모리를 반드시 해제해줍시다.
realloc 함수 |
void *realloc (void *ptr, size_t size)
realloc 함수입니다. 이 함수를 사용하면 동적메모리 할당후에 크기를 변경할 수 있습니다. malloc 과 calloc 함수는 동적메모리 할당 후에 메모리 크기를 변경하지 못했었죠.
입력 인수를 살펴봅시다. 첫번째 인수로 void형 포인터를 받고있습니다. 동적메모리의 시작주소를 의미합니다. 두번째 인수로는 size를 받고있습니다. 앞의 두 함수와는 다르게 생성될 size가 아닌 동적메모리 재할당 크기입니다.
calloc 함수와 realloc 함수를 이용하여 간단한 예제를 만들어볼까요?
#include<stdio.h> #include<stdlib.h> int main(){ int *ptr = (int*)calloc(2, sizeof(int)); //sizeof(int) = 4 바이트 만큼 2개 총 8바이트 ptr[0] = 1234; ptr[1] = 5678; ptr = (int*)realloc(ptr, sizeof(int)*4); // 총 16바이트 8바이트 늘어남. ptr[2] = 1234; ptr[3] = 4567; free(p); // 동적메모리 해제 p = NULL: return 0; }