Til New Keyword
Younji Kim / August 2022
2022년 8월 17일 Today I learned
Table of contents
new 키워드를 이용한 동적 메모리 할당
C++은 다음과 같은 세 가지 기본 타입의 메모리 할당을 지원한다.
- 정적 메모리 할당(static memory allocation) : 정적 변수(static)와 전역변수(global)에 대해 발생한다. 이러한 타입의 변수에 대한 메모리는 프로그램이 실행될 때 한 번 할당되며, 프로그램이 종료될 때까지 쭉 살아있다.
-
자동 메모리 할당(auto memory allocation) : 함수 매개 변수와 지역 변수에대해 발생한다. 특정 키워드를 입력하지 않고 일반적으로 변수를 선언할 때 일어나는 할당 종류이다. 호출 스택(call stack)의 활성화 레코드(activation record)에 저장된다. 호출 스택의 메모리는 계속 지속되지 않는데, 함수가 반환될 때 호출 스택에서 함수에 사용된 활성화 레코드는 호출 스택에서 폐기된다. 따라서 이러한 타입의 변수에 대한 메모리는 관련 블록을 입력할 때 할당되고, 블록을 종료할 때 필요에 따라 여러 번 해제된다.
- 동적 메모리 할당(daynamic memory allocation) : 프로그램 실행 중에 필요한 메모리를 운영체제에 요청하는 방법이다. 스택이 아닌 힙(heap)이라는 훨씬 큰 메모리 풀이자 영구 저장소에 할당된다. 따라서 프로그램의 제한된 스택에 할당되지 않아 할당되는 메모리 크기도 훨씬 커질 수 있으며, 할당된 메모리가 계속 지속된다. 단일 변수를 동적으로 할당하려면
new
연산자를 사용하면 된다.new int; // int형 변수를 동적 메모리 할당 int *ptr = new int; // 포인터 변수로 간접 참조 *ptr = 7; // 역참조(dereference)하여 해당 메모리에 접근해 7을 대입
배열을 선언할 때, 기존의 자동 할당은 배열의 사이즈를 const int형으로만 받았다. 그러나 안전빵으로 큰 수를 넣어 배열을 선언할 때 실제로는 쓰지 않는 공간이 많이 남으면 메모리 낭비 문제가 생긴다. 이를 방지하기 위해 동적 메모리 할당을 사용할 수 있다.
int* reverse(const int* list, int size) { int* result = new int[size]; for (int i = 0, j = size - 1; i < size; i++, j--) result[j] = list[i]; }
new int[size]
는 지정된 요소 수로 int 배열에 대한 메모리 공간을 할당하도록 컴퓨터에 알려주고, 배열의 주소를 result에 대입한다. new 연산자를 사용하여 생성된 배열을 동적 배열(dynamic array)라 한다. 이와 반대로, 일반 배열이 생성될 때에는 배열의 크기는 실행이 아닌 컴파일 시에 프로그램이 미리 알고 있어야 하므로 배열 크기에 변수를 사용할 수 없고 무조건 상수만 입력해야 한다.
new 연산자를 사용하여 할당된 메모리는 영구적이며, 명시적으로 제거(delete)하거나 프로그램이 종료될 때까지 존재한다.
동적 할당은 컴퓨터 내에서 어떻게 이루어지는가?
컴퓨터에서 응용 프로그램이 실행되면 해당 프로그램이 사용할 수 있는 메모리가 주어진다. 그 메모리는 코드 영역, 스택, 힙 등 각각 다른 용도로 나눠져 사용된다.
메모리를 동적으로 할당할 때에느느 운영 체제에 프로그램 사용을 위해 해당 메모리의 일부를 예약하도록 요청해야 한다. 이 요청을 수행할 수 있으면(프로그램이 요청하는 만큼의 크기가 메모리에 남아있을 때) 해당 메모리의 주소가 응용 프로그램에 반환된다. 그 시점부터 프로그램은 해당 메모리를 원하는 대로 사용할 수 있다. 응용 프로그램이 메모리 사용을 완료하면 이 메모리를 다시 운영체제로 반환하여 다른 프로그램에 제공할 수 있다. 사용했던 데이터를 삭제하는 과정 없이 주소를 운영체제에 돌려주면 해당 메모리는 다른 프로그램에 의해 덮어씌워질 수 있다.
댕글링 포인터
할당이 해제된 메모리를 가리키는 포인터를 댕글링 포인터(dangling pointer)라고 한다. C++은 할당되지 않은 메모리의 내용이나 삭제되는 포인터의 값에 대해서는 보장하지 않기 때문에, 댕글링 포인터를 역참조하거나 삭제하면 정의되지 않는 동작이 발생한다.
메모리를 할당 해제하면 여러 개의 댕글링 포인터가 생성될 수 있다. 의미없는 댕글링 포인터가 발생하는 것을 막기 위해 ① 여러 포인터가 같은 동적 메모리를 가리키는 것을 피해야 하며, ② 포인터를 삭제할 때 포인터를 0 또는 nullptr로 설정하는 편이 좋다.
int *ptr = new int;
// int *otherPtr = ptr; 이런 식의 이중 참조는 피하는 것이 좋다.
delete ptr;
ptr = 0; // now ptr is nullptr not dangling pointer
출처 : https://boycoding.tistory.com/204