동적할당 1, 2, 3

CJB_ny·2022년 8월 12일
0

C++ 정리

목록 보기
54/95
post-thumbnail

동적할당 1

메모리 구조 복습

메모리들어갈 녀석들
코드 영역실행 할 코드가 저장되는 영역
데이터 영역전역(global) / 정적(static) 변수
스택영역지역 변수 / 매개 변수
??


이런식으로 존나 크게 만들어 놓을 경우 한번 보자.

스택

스택은 애초에 어마어마하게 많은 데이터를 저장하고 사용하는 용도가 아니라

함수들 끼리 인자를 좀 자연스럽게 주고받기 위해서 사용하는 메모리 영역이다.

데이터 영역은?

저 코드가 실행이 되기는 하는데

문제점이 게임이 망해서 1명밖에 없더라도 500만 마리를 준비한 셈이 된다.

500만 마리를 데이터 영역에 만들면 프로그램 시작 500만마리 ~-> 프로그램 종료 까지 들고있어야됨.

그래서 HEAP

이러한 메모리 없나? => 힙

동적할당과 연관된 함수/연산자

  • malloc

  • free

  • new

  • delete

  • new[]

  • delete[]

이것들이 핵심적인 기능들이다.

어떤 컴퓨터든

운영체제실행하는 것
유저모드사용자가 사용하는 곳, LOL, 유튜브
구분구분
커널모드Windows or IOS 같은 운영체제 핵심 코드

이런 구조는 컴퓨터 뿐만 아니라 모바일 기기도 똑같다.

근데 이걸 왜 알아야하나?

LOL이라는 게임을 하다 "동적할당"이 필요한 상황이 발생한다면?

LOL이라는 프로그램에서 메모리를 만지고 조작하고 하다보면은

유튜브나 다른 프로그램에게도 영향을 줄 수도있다.

그래서 유저 영역에서 실행되는 프로그램들은

완전히 독립된 상태로 실행이 된다.

프로그램 메모리 요청시(유저모드)

유저영역의 A라는 프로그램이 메모리가 추가적으로 필요해서

메모리를 추가적으로 사용해야 한다고 할 때

"메모리"라는 것 자체는 유저모드의 각기의 프로그램들이

"관리"하는게 아니라

"커널 영역"에다가 요청을 해가지고

"커널 영역"의 허락을 받아서 "메모리"를 받아 와야한다는 것이다.

동적할당 흐름

1) 유저영역 :

운영체제 에서 제공하는 API(운영체제에서 제공하는 함수) 호출 -> 메모리를 달라고 요청

2) 커널영역 :

Windows와 같은 윤영체제관련 핵심 코드내에서

이런저런 검사를 해본 다음에 "메모리"를 적당히 할당해서 건내준다.

(실제 동작은 더 복잡한데 일단 정리하면 이런 느낌이다.)

3) 유정영역 :

ㄳㄳ 잘쓸게요~

동적할당 메모리 얼만큼 주나?

컴퓨터는 지금 유저모드의 프로그램을 몇백개씩 관리를 하는데

롤에서 메모리 필요할 때마다 커널에다가 요청하면 귀찮다.

그래서

유저모드에서 메모리를 요청할때 한방에

큼지막하게 받아 준다.

[_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ]

그러면 이제 유저모드의 A라는 프로그램에서 메모리가 필요하다고
하면은

[_ _ _] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ]

이만큼 찝어줘서 사용해~ 짤라서 쓰게된다.

이게 malloc이나 new이런 연산자와 관련이 있는 것이다.

CRT

C++ 에서 이런 힙메모리는

CRT : C런타임 라이브러리의 "힙 관리자" 를 통해 힙 영역을 사용한다.

단, 정말정말 원한다면 우리가 직접 운영체제 API를 통해

힙을 생성하고 관리할 수도있음.

-> MMORPG 서버 메모리 풀링(고급기법)

malloc

반환형이 void* 이고 인자타압이 size_t이다.

size_t

size_t정의 부분 잘라온것인데

typedef a, b하면

a라는 타입의 이름을 그냥 b로 하겠다.

unsigned __int64 를 size_t로 보겠다. 이말임.

그래서 우리가 실행하는 프로그램이 64비트이면

typedef unsigned __int64 size_t를 사용한다는 말이다.

두갈래길

unsigned 정수는 정수인데 무조건 양수만 받겠다.

void*

그러면 이렇게 메모리를 할당받고 싶은 만큼 받아오면 (1000바이트)

알아서 잘 할당해준 다음

그 시작주소를 void형 포인터로 받아 올 수 있다.

동적할당 2

malloc : 할당할 메모리의 크기를 매개변수로 넘겨준다 (바이트 단위)

메모리 할당후 시작 주소를 가르키는 void형 포인터로 반환해준다.

(만약 메모리가 부족하다면 NULL반환)

그런데 void형 포인터? ❗

일단 주소를 담는 바구니? => OK

타고가면 void 아무것도 없나? => NO

타고가면 void 뭐가 있는지 모르겠으니까 니가 적당히 변환해서 사용해라? => OK

void* ptr = malloc(1000);

1000바이트를 사용한다고 했지만

어떻게 사용을 할지는 지정을 해준 상태는 아니기 때문에

// 이렇게 메모리 주소를 void* 로받고
void* ptr = malloc(1000);

Monster m1 = (Monster*)ptr;
m1->_hp = 100;
m1->_x = 1;
m1->_y = 2;

이런식으로 형변환을 통해서 동적할당하여 몬스터 만들 수 있다.

malloc 동적할당 메모리 까보기

cdcdcdcd이런 이상한 값이 들어가있는데 (쓰레기 값)

그리고 이것을 Monster* 가정하고 값을 넣어서 사용하면은

// 이렇게 메모리 주소를 void* 로받고
void* ptr = malloc(1000);

Monster m1 = (Monster*)ptr;
m1->_hp = 100;
m1->_x = 1;
m1->_y = 2;

이런식으로 쓰레기 값에서 우리가 원하는 값으로 하나씩 변한다.

근데 지금 몬스터 멤머 변수 정수형 3개라 12바이트인데

1000바이트 할당? => 메모리 낭비

void* ptr = malloc(sizeof(Monster));

=> 굿.

free

macoll 혹은 기타 calloc, realloc 등을 통해서 할당된 영역을 해제를 할 때 사용한다.

힙관리자가 할당/미할당 여부를 구분해서 관리.

메모리를 실컷 사용하다가 필요없으면 이렇게 해제를 해준다.

이렇게 사용하다가 free해주면

디버그라 모드라 ddddddd로 밀어줌.

free가 알아서 잘 해제 하는 이유?

malloc같은 경우 12바이트라고 명시를 해주었는데

free는 그런인자 안받고 시작주소만 받고도 잘 해제를 한다.

자세히 보면은

c라고 12바이트라고 앞에 적혀있는데

malloc에 12바이트 넣어주면은 컴파일러가 컨닝 하기 위해서

[ [ 12 ] 사용하는곳 (12바이트) ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ ]

힙관리자가 이렇게 헤더에 넣어 준 것이다.

Heap Overflow

유효한 힙 범위를 초과해서 사용할 경우

free안해주면 "메모리 누수" 발생한다.

=> 메모리 꽉차면 크래쉬 난다.

double free

: 해제한거 다시 해제함 => 바로 클래쉬나서 찾기는 쉽다.

Use-After-Free

가장 끔찍한 상황이다.

지금 할당을 받아서 사용하다가 free로 해제해 주었는데

현재 void* pointer라는 녀석은 그 해제된 주소를 계속 가지고있다.

그렇다 보니까 pointer를 통해 접근이 가능함. 무섭단다 ㄷㄷ.

메모리가 날라갔지만 이렇게 또 건드릴 수 있다.

다른 유저의 골드나 경험치 같은 부분을 건들릴 수도있다.

바로 크래쉬가 안나고...

그래서 해제 한 뒤에는

이렇게 nullptr로 밀어줘야한다.

동적할당 3

malloc / free 는 C/C++ 공용으로 사용가능.

이녀석 두개는 사실 "함수"이다 ❗❗❗

(고전적 방식)

new / delete

C++에서 추가됨.

이녀석들은 "연산자"이다 ❗❗❗

뭐 사용방법은 비슷하고

double delete, Use-After-Feee 도 똑같이 발생할 수 있다.

new [] / delete []

배열 문법과 유사한 방식으로

Monster* m3 = new Monster[5];

// void* ptr = malloc(12 * 5); 랑 같다.

짝꿍을 잘 맞춰 줘야한다.

몬스터 나머지 4마리는 ❓❓

몬스터를 5마리 만들려고 new Monster[5]; 했었는데

Monster* m3 = new Monster[5];
m3->_hp = 100;
m3->_attakc = 200;

// 나머지 애들은 Monster자료형의 크기 만큼 한칸 건너뛰기위해서

Monster* m4 = (m3 + 1);
m4->_hp = 100;
m4->_attack = 300;

// 이런식으로 사용이 가능함.

또한 void* ptr = new Monster;

했을 때 처럼 메모를 들어가보면

표지판 처럼

크기를 나타내주는 부분이 있고 그다음 메모리들이 있음.

[ [ 12(Monster크기) ] hp, _attack, _defense ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ]

이런식으로'

Monster* m = new Monster[5];해도 똑같다.

malloc/free VS new/delete

  • 사용 편의성 : new/delete 승!

  • 타입에 상관없이 특정한 크기의 메모리 영역을 할당 받으려면? : malloc/free 승!

가장 중요한 차이는 ❓❓❓

malloc/free : 함수

new/delete : 연산자

이런 차이점도 중요하지만

new/delete일 경우 생성타입이 클래스일 경우 생성자/소멸자를 호출해준다!

이녀석은 그냥 메모리만 할당할게~ 느낌이 강해서

생성자랑 소멸자 호출이 안되고

OOP와 직접적으로 연관된 new/delete사용하면

생상자와 소멸자가 호출이 된다.

근데 이게 당연한게

C언어에서는 클래스와 같은 객체 지향적인 그런게 없어서

호출이 안되는게 어떻게보면 당연하다.

그렇다고 malloc사용한다고해서 생성자랑 소멸자 사용할 수 없는 것은 아니다.

new [] 했는데 delete 한경우

이렇게 여러개를 동적 할당 받아 만들었을 경우

delete만 해줄 경우 바로 크래쉬난다.

new / delete = 생성자 호출 / 소멸자 호출

이거를 짝을 맞춰 주어야한다.

항시 메모리 오염 주의 해야한다.

profile
https://cjbworld.tistory.com/ <- 이사중

0개의 댓글