[C]구조체(struct)[2] - 패딩
- 이번 포스트는 구조체의 패딩에 관한 내용입니다.
1️⃣ 패딩이란?
패딩이란 구조체 멤버변수들을 메모리에서 CPU로 읽을 각 시스템의 워드(word) 경계에서 읽어오는 것이 효율적이기 때문에 컴파일러가 (성능상의 이유로)레지스터의 블록에 맞춰 최적화 하는 작업입니다.
2️⃣ 구조체에 패딩이 생기는 과정
< 다양한 자료형을 가진 구조체의 메모리 >
#include <stdio.h>
typedef struct {
int num[6];
} info_t; // 24bytes
typedef struct {
unsigned int id; // 4bytes
info_t info; // 24bytes
unsigned short height; // 2bytes
float weight; // 4bytes
unsigned short age; // 2bytes
} data_t;
int main(void)
{
info_t info;
data_t data;
printf("info size: %d\ndata size: %d\n", sizeof(info), sizeof(data));
}
/*---출력---*/
info size: 24
data size: 40
위의 data_t구조체
의 경우 각멤버변수 크기의 합인 36bytes
가 나와야 정상이지만 40bytes
로 크기가 잡히는 것을 확인할 수 있습니다.
구조체의 멤버변수들의 주소
가 연속적
으로 존재함을 이용해서 각 요소의 주소값의 비교
해 보았습니다.
< 각요소의 주소값을 비교 >
int main(void)
{
data_t data;
int off_id = (char*)&data.id - (char*)&data;
int off_info = (char*)&data.info - (char*)&data;
int off_height = (char*)&data.height - (char*)&data;
int off_weight = (char*)&data.weight - (char*)&data;
int off_age = (char*)&data.age - (char*)&data;
printf("off_id: %d\n", off_id);
printf("off_info: %d\n", off_info);
printf("off_height: %d\n", off_height);
printf("off_weight: %d\n", off_weight);
printf("off_age: %d\n", off_age);
}
/*---출력---*/
off_id: 0
off_info: 4
off_height: 28 //height는 2bytes인데??
off_weight: 32
off_age: 36
위와 같이 각 요소의 주소값을 비교해보니 height의 크기가 2bytes인데 4bytes로 잡혀있음을 알 수 있습니다.
이와 동일하게 age
도 2bytes크기지만 4bytes로 잡혀 있습니다.
- 메모리를 읽어올 때 각 시스템의 워드(word) 경계에서 읽어오는 것이 효율적이기 때문에 이런식으로 컴파일러가
패딩
을 넣어줍니다. 다른 말로 정렬한다(aligned)라고 합니다. - 이런식으로 바이트 정렬 요구사항 때문에 구멍이 생기게 되는데 시스템마다 메모리에 접근할 때 사용하는 주소에 대한 요구사항이 다릅니다.
- x86시스템은 4바이트(워드크기)경계에서 읽어오는게 효율적입니다.
(4바이트 경계에 정렬됩니다.(aligned)) - 요구사항이 다른 아키텍처에서 호환을 시키기 위해서는 패딩을 줄이는 것이 좋을 것 같습니다.
3️⃣ 패딩 줄이기
(방법1) 맴버변수를 재배열
< 기존 구조체 >
typedef struct {
unsigned int id; // 4bytes
info_t info; // 24bytes
unsigned short height; // 2bytes
float weight; // 4bytes
unsigned short age; // 2bytes
} data_t;
< 바이트에 맞게 재배열한 구조체 >
typedef struct {
unsigned int id; // 4bytes
info_t info; // 24bytes
unsigned short height; // 2bytes
unsigned short age; // 2bytes
float weight; // 4bytes
} data_t;
- 이런식으로 재배열하는 것으로도 패딩이 없어졌습니다.(2bytes의 short변수 2개가 4bytes로 합체 되었습니다.)
- 하지만 이런식으로 일리리 4바이트 경계를 살피는 것은 다소 귀찮을 수 있습니다.
(방법2) "#pragma pack"을 사용
#pragma pack(push, 1)
typedef struct {
unsigned int id; // 4bytes
info_t info; // 24bytes
unsigned short height; // 2bytes
float weight; // 4bytes
unsigned short age; // 2bytes
} data_t;
#pragma pack(pop)
/*--사이즈--*/
sizeof(data_t) = 36
#pragma pack
을 사용하면 패딩없이 36의 크기로 메모리가 잡힙니다.- 하지만
C표준 문법
이 아닙니다. 요즘 나온 컴파일러들이 지원해주는 기능입니다.
(표준이 아니기에 모든 플랫폼에서 공통으로 호환하지 않습니다.)
(방법3) 패딩을 명시적으로 삽입
어쩔 수없이 패딩이 생길 거라면 구조체에 패딩을 명시적으로 넣는 것이 좋습니다.
typedef struct {
unsigned int id;
info_t info;
unsigned short height;
char unused[2]; //패딩을 명시적으로 삽입
float weight;
} data_t;
< assert()를 사용한 구조체 크기를 확인 >
#include <assert.h>
assert(sizeof(data_t) == 36);
- 디버그모드에서
assert함수
로 패딩을 잡아낼 수 있습니다. - 특히, 바이트 단위로 저장해서 다른 실행파일과 공유를 해야할 때 위와같이 구조체의 크기를 체크하는 것이 좋습니다.