개발 Study

#18 구조체(2) 및 동적할당

HYuk 2021. 4. 15. 19:54
728x90
struct test
{
	char a;
	int i;
	double d;
};

# 구조체의 크기

구조체의 크기는 일반적으로 생각하면 안된다.

위와 같은 구조체가 있을 때,

해당 구조체의 크기는 16이 된다.

일반적으로 생각 했을 때,

char // 1바이트

int // 4바이트

double // 8바이트

다 더하면 13바이트라고 생각 할 수 있지만, 구조체 크기의 할당은 아래와 같이 진행된다.

1. 가장 크기가 큰 멤버 변수를 기준으로 메모리 크기가 생성된다.

2. 구조체 멤버 변수들을 선언 순서대로 메모리를 할당한다.

3. 메모리가 부족하면 기준대로(크기가 가장 큰 멤버 변수 크기) 메모리 크기가 생성된다.

4. 다시 메모리를 할당한다.

 

맨 위의 코드는

double 이 가장 큰 변수니까 8바이트를 기준으로 생성된다.

8바이트에 char(1byte)와 int(4bytes)를 할당한다.

나머지 3bytes에 double이 들어갈 수 없으므로 8바이트를 다시 생성해준다.

그 8bytes에 double를 할당한다.

-> 따라서 위 코드 구조체의 크기는 8bytes * 2 => 16bytes가 되는것이다.

 

이때, 첫번째 8바이트에 남은 3바이트를 byte padding 이라고 한다.

byte padding 은 더이상 사용하지 않는 메모리 공간이 된다.

 

위에서 2. 구조체 멤버 변수들을 선언 순서대로 메모리를 할당한다. 에서

선언순서대로 메모리가 할당 되기 때문에,

선언 순서에 따라 byte padding이 발생 할 수도, 안할수도 있다.

 

struct test
{
	char a;
	short s;
	int i;
	char b;
};

위의 코드에서 보면 구조체 크기는

4 [ [a(1)] [1byte padding] [s(2)] ] 

4 [ [               i(4)               ] ]

4 [ [b(1)] [   3bytes padding   ] ]

으로 총 12bytes를 차지한다.

 

char a;

char b;

short s;

int i;

순으로 선언하는게 byte padding 을 줄이는 방법이다.

작은 데이터 부터 순서대로 배열하는게 좋고, 문자배열 같은경우2^n으로 배열하는게 좋다

 

배열 순서 가지고 놀다가 하나 알게된 사실은

short

int 등

2bytes 이상 차지하는 변수의 경우,

short의 경우는 메모리주소 2의배수

int의 경우는 메모리 주소 4의 배수에만 들어갈 수 있다는 것이다.

 

#include <iostream>

using namespace std;

struct test
{
	char a; //1 
	short s; //2
	char b; //1
	int i; //4
};

void main()
{
	cout << sizeof(test) << endl;
}

 

위에서 코드만 봤을 때는 8bytes를 차지한다고

생각했었지만, 출력 결과를 보니 12bytes가 나왔다.

위의 코드는 다음과 같이 메모리를 차지하게 된다.

4 [ [a(1)] [1byte padding] [s(2)] ] 

4 [ [b(1)] [   3bytes padding   ] ]

4 [ [               i(4)               ] ]

 

>> 구조체 안에 구조체를 사용할 경우 구조체 크기구조체 안에 구조체를 사용할 경우 자료 크기는해당 구조체를 펼친다음에 계산을 하면 된다.

 

 

## 동적 할당

동적할당은 사용자가 원할 때 메모리에 할당하고,원할때 메모리에서 해제 할 수 있다.이 동적 할 당은 메모리 영역중 Heap 영역을 사용한다.

 

동적 할당에는 2가지가 있다.

1. malloc(크기(byte단위))

2. calloc(개수, 크기(byte단위))

두 동적 할당에는 매개변수 개수의 차이 와 초기 값 뿐이다.

malloc 은 쓰레기 값으로 초기화 된다.

calloc 은 0값으로 초기화 된다.

둘 다 동적 배열을 만들 수 있다.

 

동적할당을 한 후에는, 해당 자료형이 void로 되어 있기 때문에 명시적 형변환이 필요하다.

 

그리고 heap 영역에 접근 하기 위해서는 포인터를 써야하는데,

사용 순서는 다음과 같다.

 

1. heap 영역에 bytes를 할당하고, 명시적 형변환 해준다.

2. 해당 주소를 포인터 변수에 저장한다.

3. 포인터변수로 해당 영역을 사용한다.

4. free()함수로 해당영역을 해지한다.

5. 포인터변수에 저장된 주소값을 nullptr로 초기화 시킨다.

 

이때 free함수 사용법은

free(주소값);

으로 사용할 수 있으며, 이 함수는 해당 주소값으로 가서 메모리를 해지하는 역할을 한다.

즉, heap영역은 할당한 것을 해지하지 않으면 컴퓨터가 꺼질 때 까지 가지고 있는데,

이는 메모리누수와 연결되기 때문에, 동적할당을 했을경우 free사용이 필수적이다.

 

nullptr도 해주지 않으면,

첫번째 동적할당 사용하다가 해지했는데

두번째 동적할당 했을 때, 같은주소가 할당 될 가능성이 있기 때문에,

따라서 메모리 해지 및 널포인터 초기화 습관을 들여야한다.

 

아래는 예시이다.

#include <iostream>

using namespace std;

void main()
{
	char* ptr = (char*)calloc(10, sizeof(char));
	// heap 영역에 char[10] 만들고 해당 주소를 포인터로 저장
	cin >> ptr;
	cout << ptr << endl;
	free(ptr);
	ptr = nullptr;
}
728x90

'개발 Study' 카테고리의 다른 글

#20 스트림 개방 및 입출력  (0) 2021.04.19
#19 동적할당 (2) 및 입출력 스트림 버퍼  (0) 2021.04.17
#17 구조체 및 구조체포인터  (0) 2021.04.14
#16. 문자 배열  (0) 2021.04.14
#15. 배열(2)  (0) 2021.04.12