묻공러
[C++ NOTE] Memory Structure
묻공러
묻지마공부
묻공러
전체
오늘
어제
  • 분류 전체보기 (487)
    • C (54)
      • [코드조선] C 핵심 (35)
      • [언어본색] C 기초 (19)
    • C++ (72)
      • [루키스] C++ (9)
      • [루키스] 콜백함수 (6)
      • [루키스] STL (8)
      • [루키스] Modern C++ (11)
      • [노코프] C++ (10)
      • [노코프] Tips (16)
      • [일지] C++ (12)
    • 자료구조 & 알고리즘 (50)
      • [코드조선] C 자료구조 & 알고리즘 (6)
      • [합격자되기] C++ 코딩테스트 (12)
      • [루키스] C++ 자료구조 & 알고리즘 (32)
    • CS (69)
      • [널널한 개발자] CS 개론 (19)
      • [혼자 공부하는] 컴퓨터 구조 (16)
      • [혼자 공부하는] 운영체제 (18)
      • [널널한 개발자] 네트워크 (16)
    • 게임 그래픽스 (46)
      • [전북대] OpenGL (25)
      • [일지] DirectX (21)
    • 게임 엔진 (124)
      • [코드조선] 언리얼 (53)
      • [코드조선] 언리얼 데디서버 (8)
      • [일지] 언리얼 (59)
      • [일지] 언리얼 (2) (3)
      • 유니티 (1)
    • 게임 서버 (17)
    • 게임 수학 & 물리 (19)
      • 게임 수학 (12)
      • 게임 물리 (7)
    • GIT & GITHUB (4)
    • 영어 (18)
      • [The Outfit] 대본 공부 (11)
      • the others (7)
    • 그 외 (14)
      • In (5)
      • Out (5)
      • Review (4)

인기 글

최근 글

hELLO · Designed By 정상우.
C++/[노코프] C++

[C++ NOTE] Memory Structure

2024. 3. 24. 19:10

01. Variables in Memory

# 스택 메모리와 변수

스택 메모리에 변수가 저장되는 방법은

스택 메모리의 가장 위 부분부터 데이터를 하나씩 쌓아가고

스택이 쌓이는 방향은 메모리의 주소가 감소하는 방향으로 쌓인다

 

# 예시

#include <iostream>

int main()
{
	int a = 0;
	int b = 0;
	int c = a+b;
	double d = 2.5;

	std::cout << "a:" << (long)&a << std::endl;
	std::cout << "b:" << (long)&b << std::endl;
	std::cout << "c:" << (long)&c << std::endl;
	std::cout << "d:" << (long)&d << std::endl;

}

실제로 메모리를 까보면 위 그림과 같이 저장된다

스택이 쌓일 때는 &a 부터 밑에서 위로 쌓이게 되고

메모리의 내부를 보았을 때는 &d가 먼저 보인다는 사실을 알 수 있다 

 


 

02. Variable Types 1

# Fixed width integer types

컴파일러와 아키텍처에 따라서 int, unsigned int와 같은 변수들은

최소 16비트(2바이트)를 차지하기도 한다

이를 해결하는 방법은

1) assert를 거는 방법

#include <iostream>

int main()
{
	static_assert(sizeof(int) == 4, "int is 4bytes");
	int a = 0;
    
	std::cout << sizeof(int) << std::endl;
	std::cout << sizeof(a) << std::endl;
}

 

2) 고정적인 데이터 크기를 가지는 변수(Fixed width integer types)를 사용

#include <cstdint>
#include <iostream>

int main()
{
	std::cout << sizeof(int8_t) << std::endl;
	std::cout << sizeof(int64_t) << std::endl;
}

 

# array의 size

배열은 데이터의 개수만큼 데이터 크기가 증가한다

아래의 예시에서는 총 5 *4 바이트를 차지하게 된다

#include <array>
#include <cstdint>
#include <iostream>

int main()
{
	//int a[10];
    
	std::array<int, 5> b;
	std::cout << sizeof(b) << std::endl;
}

 

# 포인터의 size

포인터는 주소를 담는 변수이고

주소는 크기가 변하지 않기 때문에

그 어떤 포인터라고 하더라도

운영체제에 따라서 4바이트 혹은 8바이트의 크기를 가진다

#include <cstdint>
#include <iostream>

int main()
{
	uint64_t ui8;

	uint64_t * ui64ptr = &ui8;
	std::cout << sizeof(ui64ptr) << std::endl;
	std::cout << (uint64_t)ui64ptr << std::endl;
}

 


 

03. Variable Types 2

# Struct의 size

구조체는 내부 변수들이 패딩되면서 차지하는 데이터 크기만큼 가진다

 

# Class의 size

클래스는 멤버 변수에가 차지하는 데이터 크기만큼 가진다

#include <iostream>
class Cat
{
public:
	void printCat();
private:
	int age;
	int weight;
};
int main()
{
	Cat cat1;
	Cat cat2;

	Cat * catPtr = &cat1;
	std::cout << sizeof(catPtr) << std::endl;
	
	return 0;	
}

 



04. Stack Frame

# 스택 프레임

스택 메모리는 변수 단위로 쌓여가는 것이 아닌

스택 프레임 단위로 쌓여가고

이러한 스택 프레임은 function(함수) 단위가 된다

 

# 스택 프레임 예시 1

void foo()
{
	int a;
	int b;
};

void bar()
{
	float c;
	double d;
};

int main()
{
	int a;
	double d;

	foo();
	bar();

	return 0;
}

 

# 스택 프레임 예시 2 - 클래스

스택 프레임이 쌓여가는 도중에

멤버 변수들을 접근 및 제어하기 위해서

클래스 멤버 함수는 해당 클래스를 가리키는 this라는 주소를 가지고 있다

class Cat
{
public:
	Cat()
	{
		m_age = 1;
	};
	~Cat(){};
	void addAge(int arg)
	{
		m_age += arg;
	};
private:
	int m_age;
};

int main()
{
	Cat cat;
	cat.addAge(10);

	return 0;
}

 



05. Heap Intro

# 힙과 스택의 관계

int main()
{
	int * ip;
	ip = new int;
	*ip = 100;
	delete ip;

	return 0;
}

 

 

# 힙이 필요한 이유

1) Life Cycle

스택은 스택프레임 단위로 계속 만들어지고 지워지기 때문에

Life Cycle 관리가 어렵다

반면, 힙은 직접 Life Cycle을 관리할 수 있다

 

2) Large Size

큰 사이즈의 데이터를 만드려고 한다면

스택에 큰 사이즈의 데이터를 만드는 것은 한계가 있다

반면, 힙을 이용해서

스택에는 힙의 포인터만 들고 있고

힙에 큰 사이즈의 데이터를 만드는 것은 가능하다

 

3) Dynamic (runtime)

스택 프레임은 크기가 컴파일 시에 모든 것이 결정된다

하지만 컴파일 시에 알 수 없는 정보를 사용할 때는

힙을 이용해서

런타임 중에 크기를 결정 및 관리할 수 있다

ex. 동적 배열 생성 가능

 



06. Heap Code

# c 스타일의 Heap 코드

c 스타일의 Heap 코드는 malloc과 free이다

이러한 코드는 class 객체 생성이 안 되는 문제점을 지니고 있어서 사용하지 않는 것이 좋다

 

# c++ 스타일의 Heap 코드

c++ 스타일의 Heap 코드는 new와 delete이다

이러한 코드는 delete를 작성해주지 않는 경우 발생하는 메모리 릭에 대처하기 번거롭다

 

# Safer c++ 스타일의 Heap 코드

따라서,

delete를 해주지 않아도 되는

Safer c++ 스타일의 코드를 사용하는 것이 좋다

Safer c++ 스타일의 코드는 스마트 포인터(변수) 혹은 벡터(배열)를 사용하는 것이다

 

벡터 자체는 스택 영역에 저장되고
힙 영역에서 할당된 메모리의 시작 주소를 저장하기 위한 포인터 (보통 4바이트)
벡터의 크기(Size)를 저장하기 위한 포인터 (보통 4바이트)
벡터의 용량(Capacity)을 저장하기 위한 포인터 (보통 4바이트)로 구성된다
그리고 벡터의 내부 객체들은 동적으로 할당된 힙 영역에 저장된다

#include <iostream>
#include <memory>
#include <vector>
class Cat
{
public:
	Cat()
	{
		std::cout << "meow" << std::endl;
	};
	~Cat()
	{
		std::cout << "bye" << std::endl;
	};
};
int main()
{
	// Safer C++ style heap Class
	std::unique_ptr<Cat> catp = std::make_unique<Cat>();
	// Safer C++ style heap Class array
	std::vector<Cat> cats(5);

	// Safer C++ style heap int
	std::unique_ptr<int> ip = std::make_unique<int>();
	*ip = 100;
	// Safer C++ style heap array
	std::vector<int> ints(5);
	ints[0]= 100;
}

 



06. Heap in Process

# Stack과 Heap의 내부

스택은 위에서 아래 방향으로 촘촘하게 내려온다

중간이 비어있지 않은 형태이다

 

힙은 아래에서 위 방향으로 여유롭게 올라간다

중간이 비어있는 형태이며

힙의 사용이 많아질수록 중간은 더 비어질 수도 있다

 

또한,

스택 프레임 내부의 순서는 컴파일러마다 다를 수 있다

힙 메모리의 공간 차이도 컴파일러 마다 다를 수 있다

 



07. Heap, Stack, Static

# 힙 vs. 스택

Stack

stack frame 단위로 저장

메모리 할당/해제 속도가 빠르다

큰 메모리 할당을 할 경우, stack overflow 발생한다

 

Heap

메모리 할당/해제 속도가 느리다

dynamic 할당이 가능하다 (런타임 중에 가능)

큰 메모리 할당이 가능하다

 

# 힙 vs. 스택 - 언제 사용하나

일반적인 경우는 Stack

 

크지 않은 메모리의 경우는 Stack

큰 메모리의 경우는 Heap

 

클래스의 경우는 일반적으로 Stack

클래스 배열의 경우는 vector를 사용해 Heap

메모리 할당/재할당이 빨라야 하는 클래스 배열의 경우는, array를 사용해 Stack

 

런타임 중에 메모리의 크기가 정해지는 경우에는 Heap

 

아래는 클래스 배열에서 vector 혹은 array를 사용하는 예제이다

#include <iostream>
#include <vector>

extern class Cat;

void foo(int num)// 할당/재할당 속도가 빠르지 않아도 되는 경우
{
	std::vector<Cat> cats(num);
	
	// do some computation over cats
};

bool bar(int num)// 할당/재할당 속도가 빨라야 하는 경우
{
	if (1000 < num)
		return false;

	std::array<Cat, 1000> cats;

	// do some computation over cats
};

int main()
{
	int count;
	std::cin >> count;
	foo(count);
	bar(count);

}

 

# Static(Global) 영역

전역 변수와 정적 변수는 Heap 아래 공간에 저장된다

 



08. Object Creation

Java에 익숙한 개발자는 new를 사용해서 객체를 생성한다

하지만 C++에서는

new를 사용하면, 메모리 릭 문제가 있고 allocation/disallocation이 느리다

 

따라서,

클래스를 생성할 때는

가급적 스택 위에 올리는 것이 좋고

힙에 올려야 한다면,

new를 쓰지 말고 스마트 포인터나 std::vector를 사용하는 것이 좋다

저작자표시 비영리 변경금지

'C++ > [노코프] C++' 카테고리의 다른 글

[C++ NOTE] Smart Pointer  (0) 2024.03.29
[C++ NOTE] Inheritance  (0) 2024.03.28
[C++ NOTE] OOP  (0) 2024.03.27
[C++ NOTE] Resource Move  (0) 2024.03.26
[C++ NOTE] Compile Process  (0) 2024.03.25
'C++/[노코프] C++' 카테고리의 다른 글
  • [C++ NOTE] Inheritance
  • [C++ NOTE] OOP
  • [C++ NOTE] Resource Move
  • [C++ NOTE] Compile Process
묻공러
묻공러
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.