2024
11.28

 

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 키워드를 변수 선언 앞에 붙여줘서 멤버별로 정렬을 제어할 수 있다. 

 

하지만 성능을 저하시킬 수 있으니 주의할 것.

 

 

COMMENT