C++에서 구조체는 데이터들의 집합이라고 알려져있다.
여러 자료형들을 담아서 이름을 붙인 것에 지나지 않는데,
구조체의 특이한 점은
메모리에 할당될 때 할당되는 메모리의 크기가 단순 내부에 있는 변수들의 크기의 합과 다르다 점이다.
사실 구조체만의 특징이라기 보다는 자료를 다루는 구조적 특성이라고 할 수 있기 때문에 클래스에도 적용되는 내용.
개별 자료형의 크기는?
전체 자료형의 크기는 문서를 참고하자.
msdn : C++ 자료형 크기
https://learn.microsoft.com/ko-kr/cpp/cpp/data-type-ranges?view=msvc-170
int 4 바이트
char 1바이트
short 2바이트
__int64 8바이트
float 4 바이트
double 8바이트
구조체의 크기는 어떻게 알 수 있을까?
sizeof() 연산자를 사용하면 된다.
int main() {
printf("사이즈A : %d", sizeof(A));
return 0;
}
테스트 1
class A
{
int a;
int b;
int c;
};
int는 4바이트를 차지하니 총 12바이트의 크기를 보인다.
테스트 2
class A
{
int a;
int b;
double c;
};
int는 4바이트 2개 + double 8바이트 1개를 차지하니 총 12바이트의 크기를 보인다.
테스트3
class A
{
int a;
double b;
int c;
};
int는 4바이트 2개 + double 8바이트 1개를 차지하니 멤버변수는 총 12바이트의 크기를 보인다.
하지만 결과값은 24바이트가 나온다.
메모리 정렬과 패딩 바이트
cpu가 메모리를 다룰 때 특정 단위(4바이트/8바이트) 단위로 접근하니 데이터가 정렬되어있다면 더욱 빠르게 접근할 수 있다.
변수를 저장할 때, 특정 주소부터 뒤쪽으로 데이터를 채워나가는데,
구조체에 포함된 가장 큰 원소의 배수로 시작위치로 정렬되어야 빠르게 접근이 가능하다.
변수마다 각자 자신의 자료형 크기에 맞게 정렬되어야하며, 더 작은 경계부분에는 들어가지 않는다.
- char(1바이트): 정렬 필요 없음.
- short(2바이트): 2바이트 경계에 정렬.
- int(4바이트): 4바이트 경계에 정렬.
- double(8바이트): 8바이트 경계에 정렬.
메모리 정렬 예시
class A
{
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
char d; // 1 byte
};
이 경우 24바이트가 나온다.
위의 구조체에서 8바이트의
1. char 형이 메모리의 첫번째 위치에서 자신의 크기인 1바이트 만큼을 차지한다.
주소 (byte) | 내용
---------------------
0 | a (1 byte)
2. double형이 진입하려고 보니 자신이 필요한 경계인 8바이트 경계에는 char형이 차지하고있다. double형은 다음 메모리 주소를 찾아간다.
주소 (byte) | 내용
---------------------
0 | a (1 byte)
1-7 | padding (7 bytes)
8-15 | b (8 bytes)
3. char형인 c가 진입하려고 그 다음주소인 16번을 찾아가서 자신을 할당한다. 구조체 전체는 가장 큰 멤버인 double형의 8바이트에 정렬을 맞춰야 하므로, 7바이트의 패딩을 추가한다.
주소 (byte) | 내용
---------------------
0 | a (1 byte)
1-7 | padding (7 bytes)
8-15 | b (8 bytes)
16 | c (1 byte)
4. char형인 d는 경계선이 1바이트기 때문에 c 바로 다음에 위치할 수 있다.
주소 (byte) | 내용
---------------------
0 | a (1 byte)
1-7 | padding (7 bytes)
8-15 | b (8 bytes)
16 | c (1 byte)
17 | d (1 byte)
5. 마지막으로 구조체 전체의 가장 큰 멤버인 double형의 8바이트에 정렬을 맞춰야 하므로, 6바이트의 패딩을 추가한다.
주소 (byte) | 내용
---------------------
0 | a (1 byte)
1-7 | padding (7 bytes)
8-15 | b (8 bytes)
16 | c (1 byte)
17 | d (1 byte)
18-23 | padding (6 bytes)
메모리 최적화
예시에서 볼 수 있 듯이 변수의 정렬 순서에 따라서 할당되는 메모리가 달라진다.
때문에 가장 큰 변수를 앞으로 정렬하는 등의 수동적인 최적화가 가능하다.
문제점
운영체제가 32비트, 64비트 인지 등에 따라 메모리 최적화는 달라질 수 있다.
즉, 최적화를 위해서 메모리 구조가 파편화되어있다는 것을 위미한다.
다른 플랫폼간의 통신이 이루어진다면 서로 간 메모리 구조가 다르기 때문에 문제가 될 수 있다.
이를 위해 패딩을 제어하는 방법도 존재한다.
#pragma pack 지시어를 사용하여 구조체의 정렬 기준을 강제로 설정하거나
alignas 키워드를 변수 선언 앞에 붙여줘서 멤버별로 정렬을 제어할 수 있다.
하지만 성능을 저하시킬 수 있으니 주의할 것.