c++ 구조체에서 패딩 && 구조체 멤버 맞춤(struct member alignment)


#pragma pack(push, 1)

typedef struct explain
{
	char a;
	int b;
	short c;
}Explain;

#pragma pack(pop)

c++를 공부하다 위와 같이 #pragma pack(push, 1) <-> #pragma pack(pop)

코드를 보았다. 위 pragma가 무슨 뜻인지 궁금해서 검색을 해봤다.



위 내용을 하기 앞서 구조체의 성질에 대해 알아야 한다.

구조체는 패딩에 의해 자료형들의 선언 순서에 따라 크기가 달라진다.



struct Test1
{
	char a;
	short c;
	int b;
};

struct Test2
{
	char a;
	int b;
	short c;
};

int main()
{
	cout << sizeof(Test1) << endl;
	cout << sizeof(Test2) << endl;

	return 0;
}

위 코드에서 Test1과 Test2의 크기는 1(char) + 2(short) + 4(int) = 7byte로 같아야 하는 것처럼 보인다 하지만 실제 결과는

출력 구조체 패딩 결과


Test1 구조나 Test2 구조나 모두 7byte가 아닌 8byte와 12byte가 나옴을 알 수 있다.



구조체가 메모리에 잡히는 규칙 0

  • 메모리에 공간이 잡힐 경우 연속적으로 잡힌다.

마치 배열이 메모리에 잡히는 것과 유사하다. 구조체도 마찬가지로 변수들의 값이 연속적인 메모리 공간에 할당되어 있다. (char a)(int b)(short c)가 나란한 공간에 잡혀 있다.


구조체가 메모리에 잡히는 규칙 1

  • 구조체가 가진 자료형 중 가장 큰 자료형이 기준이 되어 메모리에 공간이 잡힌다.

위 Test1과 Test2의 경우 int 자료형이 4byte로 가장 큰 자료형이되어 4byte가 메모리에 공간을 잡는 기준이 된다.


구조체가 메모리에 잡히는 규칙 2

  • 구조체에 선언된 변수의 순서대로 메모리에 저장한다

Test1과 Test2 모두 4byte가 공간을 잡는 기준이 된다.

  • Test1의 경우 : 메모리에 4byte가 우선 잡힌다(첫번째 4byte). char a가 먼저 들어간다. 3byte의 공간이 남게 된다(a가 4byte 중 1byte를 차지). 여기에 다시 short c가 남은 3byte 중 2byte를 차지한다. 4byte에 현재 1byte만 남은 상태이다. 이 1byte에 int b를 넣으려니 공간이 부족하다. 다시 4byte를 잡는다(두번째 4byte). 이 4byte에 int b를 넣는다. 따라서 Test1 구조체의 크기는 8byte가 된다.


  • Test2의 경우: 메모리에 4byte가 우선 잡힌다(첫번째 4byte). char a가 먼저 들어간다. 3byte의 공간이 남게 된다. 여기에 int b를 넣으려 시도한다. 공간이 부족하니 4byte 공간을 새로 잡는다(2번째 4byte). short c를 넣으려는데 공간이 없다. 다시 4byte 공간을 새로 잡는다(3번째 4byte). 3번째 4byte에 short c를 넣는다. 따라서 Test2의 구조체 크기는 12byte가 된다.


Test1의 경우는 1byte만큼 과도하게 메모리에 잡혀있고 Test2의 경우는 5byte만큼 과도하게 메모리에 잡혀있다. 구조체가 한 두개일 때는 문제가 없지만 구조체 개수 많아지면 문제가 발생한다 특히 네트워크 통신을 할 때, 패킷의 사이즈를 작게 만들면서 정확하게 사이즈를 지정하고 싶을 때 사용된다고 한다.


해결 방법

1.
구조체 안에 변수를 선언 할 때 크기가 작은 자료형 -> 크기가 큰 자료형 순으로 선언한다. 위의 예시에서 살펴 봤듯이, 구조체는 변수를 선언하는 순서가 중요하다. 따라서 크기가 작은 자료형부터 크기가 큰 자료형 순으로 구조체를 선언 했다면 패딩으로 인한 문제가 줄어든다


2. 맨 처음 #pragma pack(push, 1)와 #pragma pack(pop)를 사용한다. #pragma pack(push, 1)를 사용하게 되면 구조체의 기본 단위가 1byte가 된다.(push 옆에 1이라고 지정했으므로). 따라서 패딩 문제가 발생하지 않고 Test1과 Test2 모두 우리가 원래 계산했던 대로 그 크기가 7byte로 나오게 된다.


#pragma pack(push, 1)

struct Test1
{
	char a;
	short c;
	int b;
};

struct Test2
{
	char a;
	int b;
	short c;
};

#pragma pack(pop)

int main()
{
	cout << sizeof(Test1) << endl;
	cout << sizeof(Test2) << endl;

	return 0;
}

위와 같이 #pragma pack(push, 1)와 #pragma pack(pop) 사이에 구조체를 선언한 후 cout을 통해 확인해 보았다. 그 결과 Test1과 Test2 모두 7byte가 나옴을 알 수 있다.



참고한 사이트

https://thrillfighter.tistory.com/383 https://www.codentalks.com/t/topic/4575