(전체가 아니라 C#과 차이가 있는 부분을 중심으로 요약 정리)

동적 메모리 다루기

메모리의 작동 과정 살펴보기

int i = 7;

위와 같은 로컬 변수 i를 자동 변수(automatic variable)이라 부르며, 이는 스택에 저장된다. 프로그램의 실행 흐름이 이 변수가 선언된 스코프(유효 범위)를 벗어나면 메모리가 자동으로 해제된다.

int* ptr = new int;

new 키워드를 사용하면 힙 메모리가 할당된다. 포인터 역시 일종의 변수이기 때문에 ptr이라는 변수는 스택에 저장되지만, 이 변수가 가리키는 값은 힙에 있다. (이는 C#에서도 동일한 식으로 관리된다. 힙에 할당된 데이터를 가리키는 스택 변수가 있다. 이는 scope를 벗어난 후에도 데이터를 사용하기 위해 스택과는 다른 공간에 메모리를 할당하는 것이다)

int** handle = nullptr;
handle = new int*;
*handle = new int;

위 코드는 포인터가 스택과 힙에 모두 있는 예를 보여준다. 먼저 정수 포인터에 대한 포인터를 handle이란 변수로 선언한다. 그런 다음 정수 포인터를 담는데 충분한 크기로 메모리를 할당한 뒤 그 메모리에 대한 포인터를 handle에 저장했다. 이어서 이 메모리(*handle)에 정수를 담기 충분한 크기의 힙 메모리를 동적으로 할당했다. 이렇게 되면 두 포인터 중 하나(handle)은 스택에, 다른 하나(*handle)은 힙에 존재하게 된다.

메모리 할당과 해제

new와 delete 사용법

변수에 필요한 메모리 블록을 할당하려면 new에 그 변수의 타입을 지정해서 호출한다. 그러면 할당한 메모리에 대한 포인터가 리턴된다.

물론 이 포인터를 변수에 저장하는 등 관리하는 작업은 프로그래머의 몫이다. new의 리턴값을 무시하거나 그 포인터를 담았던 변수가 스코프를 벗어나면 할당했던 메모리에 접근할 수 없는데, 이를 메모리 누수(memory leak)이라 부른다.

void leaky()
{
  new int; // 메모리 누수가 발생한다.
}

위 코드는 int를 담을 공간만큼 메모리 누수가 발생하는 예를 보여준다. 스택에서 직접적이든 간접적이든 더는 접근할 수 없는 데이터 블록이 힙에 발생하면 메모리 누수가 발생한다.

힙 메모리를 해제하려면 delete 키워드에 해제할 메모리를 가리키는 포인터를 지정한다.

int* ptr = new int;
delete ptr;
ptr = nullptr;

Note) 메모리를 해제한 포인터는 nullptr로 다시 초기화한다. 그래야 이미 해제된 메모리를 가리키는 포인터를 모르고 다시 사용하는 실수를 방지할 수 있다.

malloc()

C++은 여전히 malloc()을 지원하지만 malloc() 대신 new를 사용하는 것이 바람직하다. new는 단순히 메모리를 할당하는데 그치지 않고 객체까지 만들기 때문이다.