Untitled

https://github.com/POCU/COMP3200CodeSamples

<aside> 💡 목차


</aside>

C# & C++ UNIT

힙?

걍 런타임중 동적할당한 변수는 힙에 저장된다는거 말하는거임

Untitled

자 그러면 힙을 설명해야죠? 힙은 뭐냐면, 컴파일 때 컴파일러가 알고 있는 메모리 공간 이런 게 아니에요, 딱 실제로 이제 프로그램이 돌기 시작을 했는데 어 거기에 메모리가 얼마나 있느냐 이런 개념이죠? 그리고 실제 그 메모리가 남하고 공유가 되기도 할 거고 어떤 의미에서는 뭐 요즘은 버추얼 메모리 시스템 쓰니까 반드시 공유는 안 되지만 어쨌든 간에 OS가 지원하는 그 무수한 메모리가 힙이라고 보시면 돼요, 그래서 실행 도중에 내가 프로그램 실행할 때마다 OS가 달라질 수 있고 그 OS의 세팅이 달라질 수는 사실 있잖아요? 그럼 그 거기 꼽힌 실제 메모리도 다를 수가 있고, 그럼 그 큰 메모리들을 이제 그냥 쓰는 거예요, 근데 이 방대한 공간에서 내가 뭐 5메가 이제 할당 해야겠다 그러면 5메가 찾아야 돼요 그것도 연속적인 곳으로 여기서 2메가 여기서 떨어진 데서 2메가 가져와서 그거를 쓸 수가 없어요, 그렇기 때문에 연속된 메모리를 찾아야 되기 때문에 느리고요 그리고 프로그래머가 이건 직접 메모리를 해제해야 돼요, 
아까 스택 포인터처럼 올라갔다가 내려가는 순간 없어지는 게 아니고 컴파일러가 딱 알아서 아 여기는 8바이트가 없어지는구나 아는 게 아니기 때문에 실제 저희가 실행 도중에 new 이렇게 해서 메모리 잡아놓은 거는 프로그래머가 지워야 돼요 그래서 그렇지 않으면 내가 깜빡 잊고 안 지우고 그 포인터 잃어버렸다, 그럼 이제 메모리 누수가 발생해서 그 포인터는 다시 못 쓰죠, 그 메모리 주소는 다시 못 쓰게 되는 그래서 메모리 누수라고 해요 C++은 언매니지드 언어이기 때문에 저희가 직접 메모리를 관리해줘야 하는 거죠 그래서 힙은 이런 차이가 있고요,
힙, 스택 봤을 때 스택을 쓸 수 있으면 무조건 스택을 쓰는 게 일단은 좋아요, 근데 실행 도중에 아 이렇게 새로 만들고 막 이래야 되는 경우도 있거든요? 또 한 함수 스코프 안에서 안 끝나고 여기저기 패스하고 해야 되고 그런 경우에는 이제 힙을 쓰기도 하죠, 아니면 함수에서 반환값으로 오브젝트를 그대로 반환하면 그 값이 복사되면서 호출자한테까지 전달도 되는데, 그 오브젝트가 너무 큰 거야 그런 경우에는 차라리 힙에 오브젝트 만들고 주소 하나 반환하는 거, 즉 4바이트 반환하는 게 큰 오브젝트 반환하면서 이제 아 그 메모리 복사 많이 하는 것보다 빠르다, 그래서 그 오브젝트 크기하고도 상관이 있어요, 오브젝트가 충분히 작으면 아 최대한 스택을 많이 쓰는 게 좋다 범위에 딱 어떤 범위에서만 쓰는 오브젝트면 더더욱 그래야 된다 그 정도 이제 기억해주세요

<aside> 💡 힙 메모리는 컴파일 시점에 크기를 알 수 없는 동적 메모리 공간입니다.


힙 메모리는 프로그램 실행 중에 필요한 만큼 메모리를 할당하고 해제할 수 있습니다.

힙 메모리의 장점은 다음과 같습니다.

힙 메모리의 단점은 다음과 같습니다.

힙 메모리는 다음과 같은 경우에 사용됩니다.

힙 메모리를 사용할 때는 다음과 같은 사항을 고려해야 합니다.

결론적으로, 스택과 힙은 각각 장단점이 있으므로 상황에 맞게 적절히 사용해야 합니다.

</aside>

힙의 동작방식

<aside> 💡

Untitled

이번에는 이제 힙을 알아보죠, 음 자 일단 PrintVectors()라는 이제 함수가 있고요, a, b 전달하면 이렇게 더해서 뭐하네요, 뭐 더해서 출력하나 봐요, 그렇죠? 전달하는 건 똑같고요 여기 new Vector() 해갖고 뭐 이렇게 result 한 다음에 출력을 한대요, 사실 그다지 좋은 예는 아닌 게 함수 안에서 new 이렇게 하는 게 그렇게 좋은 예는 아니에요, 이거는 이제 조금 뒤에 다시 좀 더 설명을 드리고 아 뭐 그러면 그다음부터 이제 역시 힙, 스택 바뀌는 과정을 보여드릴 건데 new 하니까 이제 힙에서 생기는 거고요 그럼 현재 스택에 함수의 영역이 있다는 건 똑같아요, 모든 함수는 스택에서 작동을 하니까, 그렇게 생각하시면 맞고요, 이 함수 영역은 함수 안에 코드가 아니라 함수에서 필요한 데이터를 담고 있는 스택이라는 거죠, 여기는 Foo() 영역, PrintVectors() 호출하면 당연히 여기에 PrintVectors()의 영역이 생길 건데, 그 외에 new가 있으니까 힙에도 뭔가 생길 거예요

</aside>

<aside> 💡

Untitled

자 여기 생겼어요 여기 PrintVectors() 영역이 생겼는데, 거기 보면 5120이라는 게 갑자기 보여요, 이거는 5120에 가보면 이렇게 힙에 메모리가 잡혔죠? 이게 바로 new Vector() 이 new Vector() 하면 4바이트씩 2개 필요하니까 x, y 이렇게 8바이트 잡고 새로 생성한 메모리 주소가 5120이다 그래서 이제 5120을 result라는 변수에 대입했는데, 그 result라는 변수가 스택에 있는 거죠

</aside>

<aside> 💡

 문제 발생!

[‣](https://cybecho.notion.site/2272a493004849b5a9d11ba273e502aa) 문제 발생!

자 이렇게 해서 함수를 나가요 근데 이 영역은 없어졌어요 PrintVectors() 영역, 그렇죠? 그런데 아직도 메모리에는 남아있어요, 왜? delete를 안 해줬거든요, 그렇죠?

 delete를 추가해줘야함

[‣](https://cybecho.notion.site/25de273178c448d5ba7925edb74a6f71) delete를 추가해줘야함

자 제대로 고치는 것은 여기 이렇게 보이듯이 delete를 호출해야만 아까 남아있던 여기 메모리도 같이 지워줘요 그래서 이렇게 delete 호출하는 거 잊지 말아야 한다, 반드시 생각을 하셔야죠 자 그 순서를 이제 보여 드리네요 아까 그게 얘가 아니고 delete를 바꿨다는 그 코드였고요, 이렇게 들어간 다음에 delete 호출하면 싹 지워진다 이렇게 봐주시면 돼요

</aside>

<aside> 💡 내용을 토대로 글 재구성


힙(Heap) 메모리의 이해와 활용

힙 메모리란?

힙 메모리는 프로그램 실행 중에 동적으로 할당되고 해제되는 메모리 영역입니다. 스택 메모리와 달리, 힙 메모리는 컴파일 시점에 크기가 결정되지 않고 런타임에 필요에 따라 유연하게 사용할 수 있습니다.

[!note] 힙 메모리는 운영 체제가 관리하는 큰 메모리 풀에서 필요한 만큼 할당받아 사용합니다. 이는 프로그램이 실행될 때마다 사용 가능한 메모리의 양이 달라질 수 있음을 의미합니다.

힙 메모리의 특징

  1. 동적 할당: 프로그램 실행 중 필요한 시점에 메모리를 할당하고 해제할 수 있습니다.
  2. 크기 제한 없음: 이론적으로는 사용 가능한 물리적 메모리의 한계까지 사용할 수 있습니다.
  3. 수동 관리: 프로그래머가 직접 메모리를 할당하고 해제해야 합니다.
  4. 느린 접근 속도: 스택에 비해 메모리 할당과 해제에 더 많은 시간이 소요됩니다.

힙 vs 스택

특성 힙 스택
할당 방식 동적 정적
크기 제한 거의 없음 제한적
관리 주체 프로그래머 컴파일러
접근 속도 상대적으로 느림 빠름
메모리 누수 위험 있음 거의 없음
사용 용도 큰 객체, 긴 수명의 데이터 지역 변수, 함수 호출

힙 메모리의 사용 예시

다음은 C++에서 힙 메모리를 사용하는 간단한 예시입니다:

Vector* CreateVector(int x, int y) {
    return new Vector(x, y);  // 힙에 Vector 객체 생성
}

void UseVector() {
    Vector* v = CreateVector(3, 4);
    // 벡터 사용
    delete v;  // 메모리 해제
}

이 예시에서 new 키워드를 사용하여 힙에 Vector 객체를 생성하고, 사용이 끝난 후 delete를 호출하여 메모리를 해제합니다.

힙 메모리 사용 시 주의사항

  1. 메모리 누수 방지: 할당한 메모리는 반드시 해제해야 합니다.
  2. 댕글링 포인터 주의: 이미 해제된 메모리를 가리키는 포인터를 사용하지 않도록 주의해야 합니다.
  3. 적절한 사용: 작은 객체나 짧은 수명의 데이터는 가능한 스택을 사용하는 것이 좋습니다.

힙 메모리 동작 방식

힙 메모리의 동작 방식을 시각적으로 이해하기 위해 다음 다이어그램을 살펴보겠습니다:

graph TD
    A["프로그램 시작"] --> B["힙 메모리 할당 요청"]
    B --> C{"충분한 연속 메모리 존재?"}
    C -->|Yes| D["메모리 할당"]
    C -->|No| E["메모리 부족 에러"]
    D --> F["객체 생성 및 초기화"]
    F --> G["객체 사용"]
    G --> H["메모리 해제 요청"]
    H --> I["메모리 해제"]
    I --> J["메모리 반환"]
    J --> K["프로그램 종료"]

    style A fill:#0047AB,stroke:#333,stroke-width:2px,color:#FFFFFF
    style B fill:#CC5500,stroke:#333,stroke-width:2px,color:#FFFFFF
    style C fill:#006600,stroke:#333,stroke-width:2px,color:#FFFFFF
    style D fill:#551A8B,stroke:#333,stroke-width:2px,color:#FFFFFF
    style E fill:#8B0000,stroke:#333,stroke-width:2px,color:#FFFFFF
    style F fill:#CC5500,stroke:#333,stroke-width:2px,color:#FFFFFF
    style G fill:#0047AB,stroke:#333,stroke-width:2px,color:#FFFFFF
    style H fill:#006600,stroke:#333,stroke-width:2px,color:#FFFFFF
    style I fill:#551A8B,stroke:#333,stroke-width:2px,color:#FFFFFF
    style J fill:#CC5500,stroke:#333,stroke-width:2px,color:#FFFFFF
    style K fill:#0047AB,stroke:#333,stroke-width:2px,color:#FFFFFF

이 다이어그램은 힙 메모리의 생명주기를 보여줍니다. 메모리 할당 요청부터 해제까지의 과정을 단계별로 나타내고 있습니다.

[!important] 힙 메모리를 사용할 때는 항상 메모리 누수와 댕글링 포인터에 주의해야 합니다. new로 할당한 메모리는 반드시 delete로 해제해야 하며, 해제된 메모리를 가리키는 포인터를 사용하지 않도록 주의해야 합니다.

</aside>