본문 바로가기
C++/기초

C++ 기초 : 동적 할당 (2)

글: 시플마 2024. 4. 6.

아래 코드를 보시죠.

 

오류로 실행이 되지는 않지만 

만약 실행이 된다면 int형 변수 i에는 정상적인 값이 

대입될까요?

 

 

위 코드를 그림으로 표현하면 아래와 같은 상황입니다.

 

2.1이 대입된 Heap 영역 공간의 값을

int형 변수 i가 받으려고 하면 의도하지 않은 값이 

대입될 것입니다. 이처럼 동적 할당된 공간은

선언될 때 어떤 자료형으로 사용될 것인지 정하지 않기 때문에

어떤 자료형으로든 사용될 수 있다는 것이 특징입니다.

 

애초에 선언하면서 어떤 자료형으로 사용할 것인지

정하는 일반적인 변수 형태와는 다르죠.

 

 


 

 

 

동적 할당은 두 가지 특징이 있죠.

 

1. 런타임 중에 대응 가능하다.

2. 사용자(프로그래머)가 직접 관리해야 한다.(할당한 메모리를 직접 해제해야 한다.)

 

런타임 중에 대응 가능하다는 것은 코드를 작성할 때

어떻게 사용될 것인지 미리 정할 필요가 없고

런타임 중 어떤 동작에 의해 메모리가 할당될 것인지,

그 할당된 메모리가 어떻게 사용될 것인지 유동적으로 작동한다는 것이죠.

 

그렇다는 것은 일단 코드에는 메모리가 할당될 수 있다고 작성하지만

할당되지 않을 수도 있다는 것입니다. 이때 메모리가 할당되지 않으면 해제도 해주면 안되는 것이죠.

근데 이것을 컴파일러가 판단할 수는 없습니다.

컴파일러는 런타임 중에 메모리가 할당될지 안될지 모르는 상황에서,

런타임 전에 미리 해제할 수는 없는 노릇이죠.

 

 

다른 변수는 메모리 해제를 컴파일러가 알아서 해줄 수 있습니다.

 

아래와 같은 

 

Test 함수가 있을 때, 우리는 간단하게 코드 몇 줄을 작성하지만

사실은 Test 함수가 작동하면서 생기는 지역 변수 a가 할당되는 명령어와

해당 함수를 종료하면서 메모리를 해제하는 명령어가 다 포함되어 있습니다.

 

이처럼 동적 할당이 아니라면 코드에 작성된 모든 것은

무조건 할당해야 하기 때문에 미리 할당하고 해당 함수가 종료되는 순간,

그냥 해제하면 그만입니다.

 

 

하지만 아래와 같은 상황이면

 

컴파일러가 해제하는 것은 불가능합니다.

 

런타임 중 값을 입력하는 scanf_s 함수를 통해 변수 iInput에

값을 넣습니다. 해당 값이 100이면 100Byte 공간을 할당하고

int형 포인터 변수 pInt에 주솟값을 넘겨주는 코드이죠.

 

여기서 100을 입력받지 않는다면 100Byte 공간을 할당하지 않기

때문에 해제도 할 수 없습니다. 런타임 중 컴파일러가 이것을 판단할 수는 없죠.

 

 

그렇다면 직접 해제하려면 어떻게 해야 할까요?

 

free 함수를 사용하면 됩니다.

 

iInput의 값이 100이면 동적 할당된 공간을 pInt로 가리키는 코드였습니다.

이 경우 pInt의 값이 nullptr이 아니죠? 즉 동적 할당된 공간이 있다는 것이므로

main 함수 종료 전에 pInt가 가리키는 공간을 해제해야 합니다. 

 

그래서 if문을 통해 pInt의 값이 nullptr이 아니라면

free 함수를 통해 포인터 변수 pInt가 가리키는 공간을 해제하는 것입니다.

 

free 함수의 인자로 pInt를 넣었다고 해서 

포인터 변수 pInt가 해제되었다고 생각하지 않도록

주의해야 합니다. pInt가 아닌 pInt가 가리키고 있는

동적 할당된 공간을 해제하는 것입니다.

 


 

 

 

근데 굳이 메모리를 해제해야 하는 이유가 무엇일까요?

 

메모리를 할당하고 사용한 후에 이를 해제하지 않으면

해당 공간이 계속 남아 있습니다. 메모리를 계속 차지하고 있죠.

그러면 해당 메모리 공간을 사용할 수 없게 됩니다.

이것을 '메모리 누수'라고 합니다.

 

이런 공간이 점점 많아지면 사용할 수 있는 메모리가 줄어들어

결국 프로그램을 구동할 메모리 공간도 남지 않게 되는 문제가 발생합니다.

 

 

이런 메모리 누수를 방지할 수 있도록 확인해 주는 함수도 있습니다.

 

비주얼 스튜디오 프로그램 상단 중간을 보시면

Debug 모드라고 되어 있습니다. 해당 모드에서

 

사용할 수 있는 함수이죠.

Debug 모드에서 최대한 오류를 잡습니다.

 

이후에는 아래에서 볼 수 있듯이

 

Release 모드로 전환하여 빌드하고 배포하는 것이죠.

 

 

아무래도 Debug 모드는 다양한 오류를 잡기 위한 코드가 들어가기 때문에

성능이 느릴 수밖에 없습니다. 이런 것이 없어 성능이 더 좋은 Release 모드로

최종 프로그램을 만드는 것입니다.

 

 

 


 

 

 

 

C#이나 JAVA에서는 

 

위 그림처럼 해당 언어로 작성한 코드가 바로 OS에서 작동하지 않고

중간에 가상 머신을 거쳐 OS에서 작동합니다.

 

여기서 가상 머신이 알아서 메모리를 관리해 주기 때문에

프로그래머가 메모리를 관리해 줄 필요가 없습니다.

 

이러한 기능을 가비지 컬렉션(Garbage Collection, GC)이라고 합니다.

 

자동으로 관리해 준다는 점이 장점이나,

상대적으로 C++보다 느리다는 단점이 있습니다.

 

아무래도 가상 머신을 거쳐야 하므로 한 단계가 더 추가된 느낌이고,

메모리를 자동으로 관리해 준다는 것은 직접 관리할 수 없다는 것이기 

때문에 최적화하는 것이 더 어려울 수 있습니다.

 

이러한 이유로 최적화가 중요한 게임에서는

C++를 통해 프로그래밍을 하는 것입니다.

 

 

 

 

강의 출처 : https://www.youtube.com/watch?v=PFc4g8mxOiI&list=PL4SIC1d_ab-aOxWPucn31NHkQvNPHK1D1&pp=iAQB


 

 

'C++ > 기초' 카테고리의 다른 글

C++ 기초 : 가변 배열 (2)  (0) 2024.04.06
C++ 기초 : 가변 배열 (1)  (0) 2024.04.06
C++ 기초 : 동적 할당 (1)  (0) 2024.04.04
C++ 기초 : 구조체 포인터  (0) 2024.04.04
C++ 기초 : wcscmp 함수  (0) 2024.04.04