C++에서 메모리 할당 방식은 크게 두가지로 나뉜다. 바로 정적 메모리 할당(Static Memory Allocation), 동적 메모리 할당(Dynamic Memory Allocation)이다.
코드를 실행하면 컴퓨터는 수많은 동작을 하지만 우리는 메모리 할당과 관련된 RAM PCB의 Heap, Stack의 관점에서만 알아보자.
변하지 않는 크기의 메모리를 할당한다. 코드의 컴파일 시점에 사용할 크기가 정의되고, 프로그램이 실행되어 RAM에 올라갔을 때 비어있는 메모리 공간을 찾아 할당된다.
일반적인 변수 선언 방식으로 자료형과 변수 명을 이용해 선언한다.
int temp;
static float sf;
int arr[10];
변수 선언 시 메모리의 Stack 영역에 해당 변수 자료형의 크기만큼의 자원이 할당된다.
프로그램 필요에 따라 메모리를 할당/해제한다. Heap 영역을 사용하며, 개발자가 직접 관리해야 한다.
동적 메모리 할당은 malloc과 new 두 가지 방법이 있다.
실행 상황에서 메모리의 크기를 정의하고, 필요에 따라 메모리 영역을 할당받아 사용한다.
동적 메모리 할당으로 정의한 메모리 영역은 heap 공간에 필요한 만큼의 공간을 사용한다.
Byte 단위의 크기로 메모리 할당을 한다.
자료형 단위의 크기로 메모리 할당을 한다.
void* p = malloc(4); // 4 Byte 할당
int* pi = (int*)p; // 포인터 자료형 캐스팅
*pi = 10;
free(p);
어떤 자료형을 사용할지 모르기 때문에 void*자료형으로 선언해 메모리 공간을 할당해준다. 할당할 메모리 공간 크기는 malloc()의 괄호 안에 작성한다.
이후 실제로 사용할 데이터의 자료형에 맞게 포인터 자료형 변환(캐스팅)을 해야 한다.
이렇게 하면, pi 포인터를 통해 할당된 메모리 공간을 정수형 변수처럼 사용할 수 있다.
메모리 할당을 해제하기 위해 free(변수명)을 사용한다.
int* a = new int();
*a = 10;
delete a;
지정한 자료형에 맞는 크기의 메모리를 할당하고, 그 주소를 반환한다. 포인터는 선언 시 자료형을 선언하므로 형변환이 따로 필요하지 않다.
동적으로 할당한 메모리는 사용이 종료되면 자동으로 파기되지 않기 때문에 free/delete 명령어로 자원을 회수해야 한다. 자원 회수가 되지 않으면 사용 가능한 메모리 공간이 점점 줄어들기 때문에 컴퓨터의 속도가 느려질 수 있다.
free/delete 명령어를 이용해 메모리 할당 해제를 한뒤 사용이 끝난 포인터를 더 이상 사용하지 않게 하기 위해 해당 포인터에 nullptr을 대입하는 작업을 한다.
NULL은 내부적으로 0으로 선언되어 있는데, 컴파일러가 정수형으로 인식을 할 수 있어서 예상치 못한 다른 곳으로 해당 값을 넘길 수가 있다. 따라서 nullptr을 이용해 할당되지 않은 메모리 참조를 방지하고, Type checking도 가능하게 한다.
메모리 내부의 Stack도 사용 가능한 스택 범위를 넘어갈 경우 Stack Overflow가 발생한다. 이는 주로 정적 메모리 할당시 발생하는데 할당할 메모리의 크기가 Stack의 범위를 넘어갈 경우, 즉 RAM 내부 Stack의 크기인 1MB 정도의 크기(상황에 따라 크기가 달라진다)를 넘어갈 경우 Stack Overflow가 발생하면서 프로그램 실행이 종료될 수 있다.