[C/C++] 스택 메모리(Stack Memory), 힙 메모리(Heep Memory), 변수의 수명 주기(Life Cycle)

할랑말랑·2026년 3월 5일

C/C++

목록 보기
3/45

1. 스택 메모리,힙 메모리

1-1. 스택 메모리(Stack Memory)

스택 메모리는 함수 호출시 자동으로 할당되는 해제되는 메모리 공간이다. 주로 함수의 로컬 변수와 매개 변수가 저장

특징

  • 할당 및 해제 : 스택 메모리는 LIFO(Last In First Out) 방식으로 관리됩니다. 함수가 호출되면 필요한 메모리가 스택에 할당되고, 함수가 종료되면 자동으로 해제됩니다.
  • 빠른 접근 속도 : 스택 메모리는 CPU의 캐시와 가까운 메모리로, 접근 속도가 빠릅니다.
  • 크기 제한 : 스택의 크기는 일반적으로 제한되어 있으며, 너무 많은 메모리를 할당하면 스택 오버플로우(Stack Overflow)가 발생할 수 있습니다.
  • 정적 메모리 관리 : 메모리의 할당과 해제가 자동으로 이루어지므로, 프로그래머가 직접 관리할 필요가 없습니다.
#include <iostream>
using namespace std;

void func1()
{
    int a = 10; // 지역 변수 'a', stack 메모리에서 관리됨
    cout << "func1: a = " << a << endl;
} // func1()이 종료되면 'a'는 소멸됨

int main()
{
    func1(); // func1() 호출
    // 'a'는 func1() 호출 중에만 존재하고, 함수 종료 후 소멸됨
    return 0;
}

1-2. 힙 메모리(Heep Memory)

힙 메모리는 동적으로 메모리를 할당(New)하고 해제(delete)하는 데 사용하는 메모리 공간이다. 일반적으로 객체, 배열 및 기타 데이터 구조를 저장하는 데 사용

특징

  • 유연한 메모리 관리 : 필요한 만큼 메모리를 할당하고, 더 이상 필요하지 않을 때 해제할 수 있습니다. 할당은 malloc, calloc, new와 같은 함수를 사용하여 이루어집니다.
  • 느린 접근 속도 : 힙 메모리는 스택보다 접근 속도가 느리며, 메모리 단편화(Fragmentation)가 발생할 수 있습니다.
  • 크기 제한 없음 : 힙 메모리는 시스템의 물리적 메모리나 가상 메모리의 크기에 따라 제한되지만, 스택에 비해 더 큰 메모리를 할당할 수 있습니다.
  • 수동 메모리 관리 : 메모리를 할당한 후에는 직접 해제해야 하며, 이를 잊으면 메모리 누수(Memory Leak)가 발생할 수 있습니다.
#include <iostream>
using namespace std;

void func2()
{
    int* arr = new int[5]; // 힙 메모리에 정수 배열 5개 할당
    for (int i = 0; i < 5; ++i)
    {
        arr[i] = i * 10;
        cout << "arr[" << i << "] = " << arr[i] << endl;
    }

    delete[] arr; // 배열 메모리 해제
}

int main()
{
    func2();
    return 0;
}

1-3. 비교

결론

스택 메모리와 힙 메모리는 각각의 용도에 따라 적절하게 사용해야 한다. 스택은 간단한 로컬 변수와 매개변수에 적합하고, 힙은 동적으로 크기가 변할 수 있는 데이터 구조에 적합하다.

2. 변수의 수명 주기(Life Cycle)

2-1. 지역변수(Local Variable)

  • 함수나 블록 내부에서 선언되는 변수
  • 선언된 블록(스코프) 내에서만 유효
  • 스택 메모리에 할당
  • 블록이 끝나면 자동으로 소멸
void function()
{
    int x = 10; // 함수 시작 시 생성

    if (true)
    {
        int y = 20; // 블록 시작 시 생성
        cout << y << endl;
    } // y는 여기서 소멸

    // cout << y << endl; // 에러! y는 이미 소멸됨
} // x는 여기서 소멸

특징

  • 수명 : 선언된 블록이 실행되는 동안
  • 범위 : 선언된 블록 내부
  • 초기화 : 자동으로 초기화되지 않음 (쓰레기 값 가능)

2-2. 전역 변수 (Global Variable)

  • 모든 함수 외부에서 선언되는 변수
  • 프로그램 전체에서 접근 가능
  • 데이터 세그먼트에 할당
  • 프로그램 시작시 생성, 종료 시 소멸
#include <iostream>

using namespace std;

int globalVar = 100; // 전역 변수

void function1()
{
    cout << globalVar << endl; // 접근 가능
    globalVar = 200; // 수정 가능
}

void function2()
{
    cout << globalVar << endl; // 200 출력
}

int main()
{
    cout << globalVar << endl; // 100 출력
    function1();
    function2();
    return 0;
} // 프로그램 종료 시 globalVar 소멸

특징

  • 수명 : 프로그램 전체 실행 기간
  • 범위 : 프로그램 전체
  • 초기화 : 자동으로 0으로 초기화

2-3. 정적 변수 (Static Variable)

  • static 키워드로 선언되는 변수
  • 데이터 세그먼트에 할당
  • 프로그램 종료 시까지 유지

정적 지역 변수(Static Local Variable)

  • 함수 내부에서 static 으로 선언
  • 첫 호출 시 한번만 초기화
  • 함수가 끝나도 값이 유지
  • 해당 함수에서만 접근 가능
#include <iostream>

using namespace std;

void counter()
{
    static int count = 0; // 첫 호출 시 한 번만 초기화
    count++;
    cout << "호출 횟수: " << count << endl;
}

int main()
{
    counter(); // 출력: 호출 횟수: 1
    counter(); // 출력: 호출 횟수: 2
    counter(); // 출력: 호출 횟수: 3
    return 0;
} // count는 프로그램 종료 시 소멸

정적 전역 변수 (Static Global Variable)

  • 파일 외부에서 static 으로 선언
  • 해당 파일 내에서만 접근 가능
  • 내부 연결
// file1.cpp
static int fileVar = 100; // 정적 전역 변수 (file1.cpp에서만 접근 가능)
void function1() 
{
    cout << fileVar << endl; // 접근 가능
}

// file2.cpp
// extern int fileVar; // 에러! static 변수는 다른 파일에서 접근 불가
void function2()
{
    // cout << fileVar << endl; // 에러!
}

extern 키워드는 C/C++에서 여러 파일로 구성된 프로젝트에서 전역 변수를 공유할 때 사용한다.

정적 멤버 변수 (Static Member Variable)

  • 클래스 내부에서 static 으로 선언
  • 모든 객체가 공유하는 변수
  • 클래스 외부에서 정의해야함
  • 객체 없이도 접근 가능
#include <iostream>

using namespace std;

class MyClass
{
public:
    // 정적 멤버 변수 선언
    static int count;
    int id;

    MyClass()
    {
        id = count++; // 각 객체에 고유 ID 부여
    }

    // 정적 멤버 함수
    static void printCount()
    {
        cout << "생성된 객체 수: " << count << endl;
    }
};

// 정적 멤버 변수 정의 (클래스 외부)
int MyClass::count = 0;

int main()
{
    cout << "초기 count: " << MyClass::count << endl; // 0
    MyClass::printCount();

    MyClass obj1; // count = 1
    MyClass obj2; // count = 2
    MyClass obj3; // count = 3

    cout << "obj1 Count: " << obj1.count << endl; // 3
    cout << "obj2 Count: " << obj2.count << endl; // 3
    cout << "obj3 Count: " << obj3.count << endl; // 3

    cout << "obj1 ID: " << obj1.id << endl; // 0
    cout << "obj2 ID: " << obj2.id << endl; // 1
    cout << "obj3 ID: " << obj3.id << endl; // 2

    MyClass::printCount();

    return 0;
}

2-4. 동적 할당 변수 (Dynamically Allocated Variable)

  • new 키워드로 힙 메모리에 할당
  • delete로 명시적으로 해제
  • 수명을 직접 관리
#include <iostream>

using namespace std;

int main()
{
    int size = 5;
    int* arr = new int[size]; // 배열 동적 할당

    for (int i = 0; i < size; i++)
    {
        arr[i] = i * 10;
    }

    for (int i = 0; i < size; i++)
    {
        cout << arr[i] << endl;
    }

    delete[] arr; // 배열은 delete[] 사용

    return 0;
}

특징

  • 수명 : delete 호출 시까지
  • 범위 : 포인터가 유효한 곳이면 접근 가능
  • 주의 : 메모리 누수 방지를 위해 delete 해야함

2-5. 매개변수 (Parameter)

  • 함수 선언 시 정의되는 변수
  • 함수 호출 시 값을 받음
  • 스택 메모리에 할당
  • 함수 종료시 소멸
#include <iostream>

using namespace std;

void modify(int x)
{
    x = 100; // 복사본 수정
}

int main()
{
    int a = 10;
    modify(a);
    cout << a << endl; // 10 (원본 변경 안 됨)
    return 0;
}

메모리 누수

프로그램이 동적으로 할당된 메모리를 사용 후 적절하게 해제하지 않아, 더 이상 접근할 수 없게 되는 상황을 의미한다. 이러한 누수는 메모리가 점진적으로 소모되어 시스템 성능 저하를 초래하고, 프로그램이 사용할 수 있는 메모리가 부족해지는 결과를 가져올 수 있다.

원인

  • 동적 메모리 할당 후 해제하지 않음 : malloc,calloc,new등으로 메모리를 할당한 후, free 또는 delete를 호출하지 않으면 누수가 발생
#include <iostream>
using namespace std;

void func3()
{
    int* ptr = new int(20); // 힙 메모리에 정수 20 할당
    cout << "Value: " << *ptr << endl;
    // 메모리 해제를 하지 않음
}

int main()
{
    func3(); // 메모리 누수 발생
    return 0;
}
  • 참조 손실 : 할당된 메모리의 포인터를 다른 주소로 변경하거나 NULL로 설정하면 원래 메모리에 대한 참조가 사라져 해제할 수 없게 된다.
#include <iostream>
using namespace std;

void func3()
{
    int* ptr = new int(20); // 힙 메모리에 정수 20 할당
    cout << "Value: " << *ptr << endl;

    ptr = nullptr;
    // 할당한 메모리 참조가 nullptr로 바껴서 해제 불가
}

int main()
{
    func3(); // 메모리 누수 발생
    return 0;
}
  • 예외 처리 부족 : 예외가 발생했을 때, 할당된 메모리를 해제하지 않고 프로그램이 종료되면 메모리 누수가 발생할 수 있다.

결과

  • 성능 저하 : 시스템의 사용 가능한 메모리가 줄어들어 프로그램의 성능이 저하된다.
  • 크래시 : 시스템의 메모리 자원이 부족해지면 프로그램이 비정상적으로 종료될 수 있다.

방지 방법

  • 적절한 메모리 해제 : 동적으로 할당한 메모리는 반드시 사용 후 해제 해야한다.
  • 스마트 포인터 사용 : C++에서는 스마트 포인터를 사용하여 자동으로 메모리를 관리 할수있다.
  • 메모리 분석 도구 사용 : Valgrind,AddressSanitizer 같은 도구를 사용하여 메모리 누수를 감지, 분석 한다.

결론

메모리 누수는 프로그래밍에서 흔히 발생할 수 있는 문제로, 이를 방지하기 위해서는 메모리 관리를 철저히 하고, 적절한 도구를 활용하는 것이 중요하다.

0개의 댓글