Stack
- 일반 지역 변수들은 stack 메모리에 할당된다.
- 지역 변수는 해당 함수가 종료되면, 변수 선언시 할당되었던 메모리가 회수되며 소멸된다. 따라서 사용자가 따로 메모리를 관리해줄 필요가 없다.
*stack에 저장된 지역 변수
더보기

#include <iostream>
using namespace std;
void Func1()
{
int a = 10;
a++;
cout << a << endl;
}
int main()
{
for (int i = 0; i < 5; i++)
{
Func1();
}
return 0;
}

- 스택은 함수 호출 시 사용되는 임시 메모리 공간으로, 지역 변수는 기본적으로 이 스택에 저장된다.
- 함수가 종료되면 스택 프레임이 제거되면서 지역 변수도 함께 소멸된다.
*static으로 변수 선언시
더보기

void Func1()
{
static int a = 10;
a++;
cout << a << endl;
}
int main()
{
for (int i = 0; i < 5; i++)
{
Func1();
}
return 0;
}

- static 키워드로 선언된 변수나 전역 변수는 스택이 아닌 정적 메모리에 저장되므로, 함수가 종료되더라도 메모리에 남아있다. 이는 해당 값이 프로그램이 종료될 때까지 유지된다는 특징을 가진다. 따라서 static 키워드로 선언된 정적 변수 혹은 전역변수는 함수 호출 간에도 데이터를 유지할 수 있는 변수라고 볼 수 있다.
- 정적 변수는 프로그램이 시작될 때 정적 메모리의 일정 공간에 미리 자리 잡는다. 프로그램 전체에서 사용할 정적 변수의 크기는 컴파일할 때 결정되어, 너무 많이 정적 변수를 사용하게 되면 정적 메모리 영역을 초과할 수 있어 꼭 필요한 경우에만 사용 권장된다.
Heap
- 선언시 new 연산자를, 해제시에는 delete 연산자를 사용한다.
- 스택처럼 자동으로 해지하지 않아 리스크가 존재하고, 직접 해제해주지 않으면 메모리 누수 발생할 수 있다.
- 생존 주기는 사용자가 선언한 순간부터 해지하기 전까지이다.
- 메모리 공간이 커서 대량으로 메모리를 할당할 수 있고, 실행하는 도중 실시간으로 메모리 할당이 가능하다.
*new와 delete를 사용한 동적 메모리 할당
더보기

#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Employee
{
public:
Employee()
{
cout << "Employee 생성자 호출" << endl;
}
virtual void Work()
{
cout << "Employee is working" << endl;
}
virtual ~Employee()
{
cout << "Employee 소멸자 호출" << endl;
}
};
class Developer : public Employee
{
public:
Developer()
{
cout << "Developer 생성자 호출" << endl;
}
void Work() override
{
cout << "Developer is Working" << endl;
}
~Developer() override
{
cout << "Developer 소멸자 호출" << endl;
}
};
class Manager : public Employee
{
public:
Manager()
{
cout << "Manager 생성자 호출" << endl;
}
void Work() override
{
cout << "Manager is Working" << endl;
}
~Manager() override
{
cout << "Manager 소멸자 호출" << endl;
}
};
int main()
{
cout << "====정적 배열 사용====" << endl;
// Employee 배열(기본 생성자 호출)
Employee Team_Static[2];
cout << endl << "====동적 배열 사용====" << endl;
// 동적 메모리 할당
Employee* Team_Dynamic[2];
Team_Dynamic[0] = new Developer();
Team_Dynamic[1] = new Manager();
for (int i = 0; i < 2; i++)
{
Team_Dynamic[i]->Work();
}
// 동적 메모리 해제
for (int i = 0; i < 2; i++)
{
delete Team_Dynamic[i];
}
return 0;
}
*Heap 메모리에 정수형 변수와 정수형 배열 선언
더보기

void Func1()
{
int* ptr = new int(123); // 힙 메모리에 정수 123 할당
cout << "Value: " << *ptr << endl;
delete ptr; // 메모리 해제
}
void Func2()
{
int* arr = new int[5]; // 힙 메모리에 5size 배열 할당
for (int i = 0; i < 5; i++)
{
arr[i] = i * 10;
cout << "arr[" << i << "] = " << arr[i] << endl;
}
delete[] arr; // 메모리 해제
}

*사용자 입력 기반으로 Heap 메모리에 동적 배열 생성
더보기

void Func3()
{
int size;
cout << "배열의 사이즈를 입력하세요: ";
cin >> size;
int* DynamicArray = new int[size];
for (int i = 0; i < size; i++)
{
DynamicArray[i] = i * 5;
cout << "Dynamic Array[" << i << "] = " << DynamicArray[i] << endl;
}
}

*추가적으로 vector는 선언시에 객체 자체는 Stack에 저장되고, 실질적인 값의 저장은 Heap에 저장된다.
(따라서 따로 메모리에서 해제시켜 줄 필요 X)
더보기
void Func3()
{
vector<int> DynamicArray; // Stack에 객체 자체(메타데이터 포함) 저장, capacity가 0이므로 heap할당 x
DynamicArray.push_back(10); // Heap에 10 저장
DynamicArray.push_back(20); // Heap에 20 저장
}
- 데이터의 양이 증가되어 크기(size)가 용량(capacity)을 초과하거나 같다면 Heap메모리를 동적으로 확장시키고 기존 데이터를 복사해 증가된 공간에 추가된 데이터 요소를 저장한다.
void Func3()
{
int size;
cout << "배열의 사이즈를 입력하세요: ";
cin >> size;
vector<int> DynamicArray(size); // Stack에 객체 자체 / 제어 정보(포인터, 크기, 용량 등) 저장
for (int i = 0; i < size; i++)
{
DynamicArray[i] = i * 5; // Heap에 i * 5 값 저장
}
}
- 사용자 입력으로 vector의 선언과 동시에 size정보를 넘겨주면 이에 맞춰 메타데이터가 설정되고 capacity의 값도 존재함으로 heap메모리 역시 size에 맞춰 할당된다.
요약
'내배캠 > C++' 카테고리의 다른 글
템플릿 함수 (0) | 2024.12.24 |
---|---|
스마트 포인터 (0) | 2024.12.24 |
가상 함수와 추상 클래스 (0) | 2024.12.24 |
Quick Sort 정렬 알고리즘 (0) | 2024.12.23 |
유클리드 알고리즘 (1) | 2024.12.23 |