동적 할당 1 (malloc, free)

 

인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다. 
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!

 

 


 

메모리 구조 복습

 

   
 코드 영역  실행할 코드가 저장되는 영역
 데이터 영역  전역(global)/정적(static) 변수
 스택 영역  지역 변수/매개 변수
 힙 영역  동적 할당

 

문제 상황

 

문제 상황
-  MM)RPG 동접 1명~5만명, 몬스터 1마리~500만마리

-  몬스터 생성 이벤트 -> 5분 동안 몬스터가 10배 많이 나옴

 


스택 영역

-  함수가 끝나면 같이 정리되는 불안정한 메모리

-  잠시 함수에 매개변수 넘긴다거나 하는 용도로는 OK

 

메모리 영역

-  프로그램이 실행되는 도중에는 '무조건' 사용됨

 

 

희망사항)

-  필요할때만 사용하고, 필요없으면 반납할 수 있는!

-  그러면서도 (스택과는 다르게) 우리가 생성/소멸 시점을 관리할 수 있는!

-  그런 아름다운 메모리 없나? -> HEAP

  • 동적할당과 연관된 함수/연산자 : mallocfreenewdeletenew[ ]delete[ ]

 

커널 영역  vs. 유저 영역

 

유저 영역 [메모장] [LOL] [곰플레이어]
---------------------------------------
커널(Kernel) 영역 ( Windows 등의 핵심 코드)

 

유저 영역) 운영체제에서 제공하는 API 호출

커널 영역) 메모리 할당해서 건내줌

유저 영역) ㄳㄳ 잘쓸께요~

[                      ] 

-  C++에서는 기본적으로 CRT(C Run Time Library) [힙 관리자]를 통해 힙 영역 사용

-  단, 정말 원한다면 우리가 직접 API를 통해 HEAP을 생성하고 관리할 수도 있음 (MMORPG 서버 메모리 풀링)

 


 

malloc,  free 와   void*

 

   
malloc 할당할 메모리 크기를 건내준다
메모리 할당 후 시작 주소를 가리키는 포인터를 반환해준다 (메모리 부족 NULL)
free malloc (혹은 기타 calloc, realloc 등의 사촌)을 통해 할당된 영역을 해제
힙 관리자가 할당/미할당 영부를 구분해서 관리

 

void*  무엇일까?

-   * 가 있으니까 포인터는 포인터 (주소를 담는 바구니) => OK

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

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

 void* pointer = malloc(1000);

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

만약에 free 하지 않으면 메모리 누수 발생

위의 코드에서 free(pointer)가 없으면 메모리 누수가 발생한다. 

pointer를 사용하고 반환을 하지 않기 때문에 메모리를 차지하고 있어 언젠가는 메모리가 가득차 프로그램이 뻣는다. 


 

 

Double free

 

-  free를 두 번 하는 경우 대부분 크래쉬가 나고 끝난다.

#include <iostream>
using namespace std;

class Monster{
public:
    int _hp;
    int _x;
    int _y;
};

Monster monster[500 * 10000];   // 상한선 숫자로 만든다. 몬스터가 1마리이면 메모리 낭비이다

int main(){
    void* pointer = malloc(sizeof(Monster));

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

    
    free(pointer); // 만약에 free 하지 않으면 메모리 누수 발생

    // Double Free
    free(pointer); // 이건 대부분 그냥 크래시만 나고 끝난다

    return 0;
}

크레쉬 나고 끝난다.


 

Use - After - Free

 

#include <iostream>
using namespace std;

class Monster{
public:
    int _hp;
    int _x;
    int _y;
};

Monster monster[500 * 10000];   // 상한선 숫자로 만든다. 몬스터가 1마리이면 메모리 낭비이다

int main()
{
    void* pointer = malloc(sizeof(Monster));

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

    free(pointer);
    pointer = nullptr;
    m1 = nullptr;

    // Use-After-Free
    // - 프로그래머 입장 : OMG 망했다!
    // - 해커 입장 : 심봤다!
    m1->_hp = 100;
    m1->_x = 1;
    m1->_y = 2;

    return 0;
}

 


 

 

전체 코드

 

더보기
#include <iostream>
using namespace std;

class Monster{
public:
    int _hp;
    int _x;
    int _y;
};

Monster monster[500 * 10000];   // 상한선 숫자로 만든다. 몬스터가 1마리이면 메모리 낭비이다

int main()
{
    // Monster monster[500 * 10000];    //스택오버플로우(Stack Overflow). 뻗는다

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

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

    // Heap Overflow
    // - 유효한 힙 범위를 초과해서 사용하는 문제

    // 만약에 free 하지 않으면 메모리 누수 발생
    free(pointer);
    pointer = nullptr;
    m1 = nullptr;

    // Double Free
    // - 이건 대부분 그냥 크래시만 나고 끝난다
    // free(pointer);

    // Use-After-Free
    // - 프로그래머 입장 : OMG 망했다!
    // - 해커 입장 : 심봤다!
    m1->_hp = 100;
    m1->_x = 1;
    m1->_y = 2;

    return 0;
}