동적 할당 - new/delete
목차
- 01. Intro
- 02. 동적 할당
- 동적 할당이란?
- new/delete 키워드
- 동적 할당 및 포인터 주의점
- malloc/free 와 차이점
- 03. 마무리
- 04. 연관 내용
01. Intro
오늘은 포인터를 이용한 [동적 할당]에 대해 알아보겠습니다.
02. 동적 할당
동적 할당이란?
동적 할당은 프로그램 실행 중인 런타임에서 힙 메모리 영역에 할당하고 해제하는 것을 말합니다.
반대로, 컴파일 타임에서 스택 메모리 영역에 할당하고 해제하는 것은 정적 할당입니다.
추가로, 동적 할당이 정적 할당보다 속도가 더 느립니다.
동적 할당으로 연속되지 않은 메모리를 사용할 수 있고, 할당할 영역을 운영체제로부터 받아와야 합니다.
하지만, 정적 할당은 연속된 메모리 영역을 사용하고, 스택 포인터를 이용한 LIFO 구조를 가지고 있어 작동 구조가 단순합니다.
동적 할당하는 과정을 자세히 살펴보면, 아래와 같습니다.
1. new 키워드를 통해 유저 영역인 프로그램에서 운영체제의 커널 영역에게 메모리 요청을 진행합니다.
2. 이때, 시스템 호출로 유저 영역에서 커널 영역으로 요청됩니다.
3. 운영체제가 힙 영역에서 사용할 수 있는 메모리 공간(블록)을 찾아 할당합니다.
4. 유저 영역에서 사용할 수 있도록 힙 영역의 메모리 주소를 반환합니다.
위 과정에서 시스템 호출, 가상 메모리 매핑, 단편화 문제 등이 발생하면, 동적 할당 시간이 더 소요됩니다.
new / delete 키워드
C++에서는 동적 할당을 할 때, 사용하는 키워드로 new와 delete가 있습니다.
new 키워드
#include <iostream>
using namespace std;
class Sport
{
};
int main()
{
Sport* sport = new Sport();
return 0;
}
new 키워드를 통해, 해당 클래스의 객체를 생성하는 코드입니다.
Sport 객체는 힙 영역에 존재하며, sport 포인터 변수에는 Sport 객체에 대한 주소값을 가지고 있습니다.
접근 방식
힙 영역에 할당된 객체에 접근하려면, 두 가지 방법이 있습니다.
#include <iostream>
using namespace std;
class Sport
{
public:
int _x = 0;
};
int main()
{
Sport* sport = new Sport();
sport->_x = 5;
(*sport)._x = 10;
return 0;
}
1. *(접근 연산자)로 객체를 가지고 와서 .(멤버 접근 연산자)로 클래스의 멤버 변수에 접근할 수 있습니다.
2. ->(멤버 포인터 접근 연산자)로 바로 접근할 수 있습니다.
delete 키워드
delete 키워드를 통해 힙 영역에 할당된 객체를 해제할 수 있습니다.
#include <iostream>
using namespace std;
class Sport
{
public:
int _x = 0;
};
int main()
{
Sport* sport = new Sport();
sport->_x = 5;
(*sport)._x = 10;
delete sport;
return 0;
}
위 코드처럼 delete 키워드를 사용하면, 힙 영역에 할당된 Sport 클래스 객체가 메모리 해제되어 더 이상 사용할 수 없습니다.
그렇다면, '크기를 별도로 입력하지 않았음에도 메모리 해제할 때, 어떻게 객체 크기만큼 해제할까?'라는 의문점이 생겼습니다.
동적 할당할 때, 실제 데이터 공간 앞에 메모리 블록의 크기 정보를 저장하는 추가 공간을 같이 할당합니다.


그래서, sport 객체의 메모리 주소로 이동하니, sport 객체 앞에 클래스의 크기인 4바이트를 저장하는 공간이 있음을 확인했습니다.
동적 할당 및 포인터 주의점
동적 할당과 포인터를 사용할 때, 주의해야 할 부분이 있습니다.
해당 사항에 대해 자세히 알아보도록 하겠습니다.
double free
delete sport;
delete sport;
sport 객체에 대해서 메모리 해제를 했지만, 한 번 더 호출하는 경우 컴파일 에러가 발생합니다.
Use-After-Free
delete sport;
delete 키워드를 통해서 힙 영역에 할당된 sport 객체를 메모리 해제했습니다.
하지만, sport 포인터 변수에는 해당 주소값이 계속 존재합니다.
그래서, 힙 영역에 객체는 메모리 해제되었지만, 접근을 시도할 수 있습니다.
이때, 메모리 해제된 영역을 접근하면, 메모리 오염이 발생하므로 이를 반드시 방지해야 합니다.
delete sport;
sport = nullptr;
이를 해결하기 위해 메모리 해제 작업 후, 포인터 변수에 nullptr로 초기화를 합니다.
또는, 스마트 포인터를 사용해도 됩니다. 스마트 포인터는 추후에 자세히 알아보도록 하겠습니다.
댕글링 포인터 문제
댕글링 포인터는 삭제되거나 반환된 메모리를 참조하는 포인터를 말합니다.
위에서 살펴봤던 Use-After-Free 문제도 메모리 삭제 후, 접근을 시도함으로써 발생하는 문제로 댕글링 포인터의 일부입니다.
추가로, 지역 변수 주소를 반환하고, 이를 외부에서 접근할 때도 댕글링 포인터 문제가 발생합니다.
int* test()
{
int num = 100;
return #
}
위 코드처럼 지역 변수인 num에 대한 주소값을 반환하고 있습니다.
num 변수는 test 함수 내에서만 유효하므로 test 함수가 종료되면, num 변수도 스택 영역에서 제거됩니다.
와일드 포인터 문제
와일드 포인터는 초기화되지 않은 포인터입니다.
초기화되지 않은 포인터는 엉뚱한 메모리 주소값을 가지고 있을 가능성이 있습니다.
초기화하지 않고 접근할 경우, 엉뚱한 메모리 주소로 접근해 문제가 발생할 수 있습니다.
Sport* sport;
sport->_x = 100;
위 코드처럼 실행하는 경우에는 오류가 발생하게 됩니다.
malloc / free와 차이점
C언어에서는 동적 할당할 때, malloc과 free 키워드를 사용합니다.
이 두 가지 키워드와 C++의 new, delete 키워드 차이점에 대해 살펴보도록 하겠습니다.
차이점
둘의 가장 큰 차이점은 생성자 & 소멸자 호출 여부입니다.
C++의 new와 delete 키워드는 클래스 객체에 대한 생성자와 소멸자가 호출됩니다.
반면에, C의 malloc과 free 키워드는 클래스 객체에 대한 생성자와 소멸자가 호출되지 않습니다.
#include <iostream>
using namespace std;
class Sport
{
public:
Sport()
{
cout << "Sport()" << endl;
}
~Sport()
{
cout << "~Sport()" << endl;
}
int _x = 0;
};
int main()
{
Sport* sport = new Sport();
delete sport;
cout << "=================" << endl;
Sport* sport2 = (Sport*)malloc(sizeof(Sport));
free(sport2);
return 0;
}

위 코드를 실행하면, C++ 스타일의 동적 할당 키워드만 생성자와 소멸자가 실행된다는 것을 확인할 수 있습니다.
03. 마무리
오늘은 new와 delete 키워드를 통한 동적 할당에 대해서 알아봤습니다.
동적 할당을 할 때, 주의해야 할 점과 C 스타일의 동적 할당인 malloc & free 키워드와의 차이점도 같이 알아봤습니다.
04. 연관 내용
읽어주셔서 감사합니다.
틀린 내용 지적은 언제나 환영입니다!
'게임 개발 > C++' 카테고리의 다른 글
| [C++] 람다 함수 - 2. 활용 (0) | 2025.03.26 |
|---|---|
| [C++] 람다 함수 - 1. 기본 형태 (0) | 2025.03.25 |
| [C++] 중괄호 초기화 - 2. initializer_list (0) | 2025.03.20 |
| [C++] 중괄호 초기화 - 1. 사용 용도 (0) | 2025.03.19 |
| [C++] auto 키워드 (0) | 2025.03.18 |