본문 바로가기

C, C++

구조체 정렬 및 원리

struct pack {
	char a;
    int b; 
};

sizeof(pack);

여기서 sizeof(pack)의 값 즉, 구조체의 크기는 5가 될 수도 있지만 다른 값이 될 수도 있습니다.

다른 값이 나오는 이유는 구조체를 정렬하기 때문입니다. 구조체를 정렬하면 프로그램 성능 향상을 위해 구조체의 각 멤버 사이에 특정 크기의 공간을 비워둡니다.

struct pack2 {
	char a;
    char b; 
    int aa;
};

struct pack3 {
	char a;
    int aa;
    char b;
};

위의 구조체들이 4바이트 정렬돼있다고 가정했을 때의 구조는 다음과 같습니다.

 

 

(1)pack2, (2)pack3, (3)은 1바이트로 정렬되었다고 가정했을 때의 pack2입니다.

 

일단 (1)에서 a는 A 블록(처음 4바이트 경계) 안에 들어갈 수 있습니다. b는 a와 함께 들어갈 수 있습니다. 그러나 aa는 a, b와 함께 들어갈 수 없습니다. 그러므로 a, b만 넣고 A 블록의 나머지 부분 2바이트는 비웁니다. 그리고 B는 aa가 들어갑니다.

 

(2)는 a와 aa가 A 안에 못 들어가므로 a만 넣고, 나머지를 비웁니다. 그리고 B에 aa를 넣고 C에 b를 넣고 나머지 3칸을 비우는 이유는 4바이트로 정렬되었을 때 구조체의 크기는 4의 배수가 되어야 하기 때문입니다.

위와 같이 같은 멤버들임에도 선언 순서에 따라 두 구조체의 크기가 차이가 납니다.

 

(3)의 경우는 1바이트 정렬이어서 그냥 붙어있는 구조입니다.

 

또 알아두어야 될 것이 만약 8바이트 정렬일 경우에는 (1), (2) 구조가 변하지 않습니다. 왜냐하면 (1), (2)에서 제일 큰 멤버가 aa고, 크기가 4바이트 밖에 안되기 때문에 8바이트는 무시되고 4바이트 정렬이 이루어집니다.

그러므로 가장 큰 멤버가 설정한 바이트 정렬 수 보다 작을 때는 바이트 정렬 수가 무시됩니다.

 

그리고 구조체 정렬 바이트 수는 2의 제곱수(1, 2, 4, 8, …)만 설정 가능합니다.

 

구조체를 정렬하면 CPU가 구조체를 읽는 성능이 향상됩니다.

왜 구조체를 정렬하는 것이 성능 향상에 도움이 되는 것일까요?

 

CPU는 데이터를 읽을 때 한 번에 여러 바이트씩 읽습니다. 그래서 CPU마다 다르지만 4바이트씩 읽는 CPU가 있다고 칠 때,

 

구조체의 aa 멤버를 읽는다고 가정하면,

4바이트 정렬된 (1)의 경우 aa를 읽으려면 B 블록만 한 번에 읽으면 됩니다. 그래서 읽는 횟수는 1번입니다.

그런데 (3)의 경우 1바이트씩 정렬해 있어서 aa가 A와 B 사이에 겹쳐져 있습니다. 그래서 A, B를 둘 다 읽어야 비로소 aa 전체를 읽기 때문에 읽기 횟수가 2번이 됩니다. 이와 같은 원리로 구조체의 정렬에 따라 성능 차이가 나는 것입니다.

 

그리고 (2)에서 마지막 3칸을 비우는 것이 어떻게 성능 향상에 도움이 되나면 구조체 일반 변수로 정의할 때는 상관없지만 배열을 선언할 때 이전 데이터에서 3칸을 비워줘야 다음 데이터의 첫 부분이 정상적으로 4바이트 경계에 위치하게 됩니다.

 

 

구조체 정렬 방식은 기본적으로는 컴파일러 옵션으로 설정할 수 있지만, 임시적으로 해당 범위에 정렬 방식을 바꿀 수 있습니다.

#pragma pack(push,(크기))
//여기에 변경 된 설정이 적용됩니다.
#pragma pack(pop)
#pragma pack(push,1)
struct pack4 {
	char a;
    char b;
    int aa;
};
#pragma pack(pop)

또는 #pragma pack((크기)) 형식으로 밑의 한 구조체만 정렬 방식을 바꿀 수 있습니다.

#pragma pack(1)//밑의 pack5구조체만 적용됩니다.
struct pack5 {
	char a;
    char b;
    int aa;
};

※ 구조체의 멤버들의 위치를 참조하기 위해서 offsetof 매크로를 사용할 수 있습니다.

#include <cstdio> 

void main() {
	printf("%d %d %d\n", offsetof(pack2, a), offsetof(pack2, b), offsetof(pack2, aa));//0 1 4가 출력됩니다. 
}

 

※ Visual Studio에서 구조체 정렬 방식 바꾸기 : 프로젝트 속성에서 C/C++ -> 코드 생성에 '구조체 멤버 맞춤'을 변경합니다.

'C, C++' 카테고리의 다른 글

포인터  (0) 2019.12.02
공용체  (0) 2019.12.02
비트 구조체  (0) 2019.11.28
구조체  (0) 2019.11.27
main 함수  (0) 2019.11.26