다음과 같은 코드가 있습니다. 다음 코드를 실행하면 얼마가 나올까요?
#include <stdio.h>
struct Student{
int a;
char b[6];
int c;
} Std1;
int main(){
printf("Std1의 크기는 %d byte 입니다.\n", sizeof(Std1));
}
Std1의 크기는 16 byte 입니다.
우리 생각으로는 6 byte + 4 byte + 4byte = 14 byte가 나와야 할 것 같지만 실제로는 16 byte가 나왔습니다.
그 이유는 word에 있습니다.
Word란 컴퓨터에서 연산의 기본 단위가 되는 정보의 양을 의미합니다.
일반적으로 32bit CPU라고 하는 x86 아키텍처는 1Word=4byte
입니다.
64bit CPU라고 하는 x86-32 아키텍처는 1Word=8byte
입니다.
(이것은 절대적인 것은 아니니 예외가 얼마든지 있을 수 있습니다.)
컴퓨터는 word 단위로 데이터를 읽을 때 가장 효율이 좋습니다.
그래서 struct의 메모리 크기는 이 word의 배수로 잡습니다.
만약 4byte int 자료형이 최대 크기라면 4의 배수, 8byte의 double 자료형이 최대 크기라면 8의 배수로 이루어집니다.
그렇다면 4의 배수로 맞추고 남는 메모리는 어떻게 할까요? 그냥 빈 공간으로 비워둡니다. 이것을 Byte Padding
이라고 합니다.
그렇기 때문에 위의 Student 구조체의 크기는 다음과 같이 메모리가 할당됩니다.
idx | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
a | a | a | a | b | b | b | b | b | b | c | c | c | c |
여기서 눈여겨 볼 것은 b변수 6byte 뒤에 두 칸의 메모리가 비어 있다는 것입니다.
이 공간을 Padding Byte라고 합니다. 또한 가장 마지막에 Padding Byte가 생기는 것이 아니라 멤버를 선언한 순서에 따라서 생깁니다.
다음과 같은 예를 생각해 볼 수 있습니다.
#include <stdio.h>
struct Student{
int a;
char b[2];
int c;
char d[2];
} Std1;
int main(){
printf("Std1의 크기는 %d byte 입니다.\n", sizeof(Std1));
}
Std1의 크기는 16 byte 입니다.
이 경우에는 메모리는 다음과 같이 할당됩니다.
idx | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
a | a | a | a | b | b | c | c | c | c | d | d |
Padding Byte가 2byte, 2byte 총 4byte가 생성됩니다. 만약 멤버 선언 순서를 바꾸면 어떻게 될까요?
#include <stdio.h>
struct Student{
int a;
char b[2];
char d[2];
int c;
} Std1;
int main(){
printf("Std1의 크기는 %d byte 입니다.\n", sizeof(Std1));
}
Std1의 크기는 12 byte 입니다.
좀 전과 다르게 4 byte가 감소한 모습을 볼 수 있습니다.
이와 같이 메모리 배치를 고려한다면 같은 구조를 가지는 구조체라도 좀 더 효율적으로 메모리를 사용할 수 있습니다.
idx | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
a | a | a | a | b | b | d | d | c | c | c | c |
간혹 설명 중 "멤버 중 가장 큰 크기로 할당된다."라고 설명하는 경우도 있는데, 이해하기는 편하지만 엄밀히는 틀렸습니다.
#include <stdio.h>
typedef struct {
int a,b,c;
} STRUCT_A;
typedef struct{
STRUCT_A a;
int b;
} STRUCT_B;
int main(){
STRUCT_A a;
STRUCT_B b;
printf("size of A is %u byte\n", sizeof(a));
printf("size of B is %u byte\n", sizeof(b));
}
size of A is 12 byte
size of B is 16 byte
위와 같은 경우가 있기 때문입니다.
STRUCT_A의 크기는 12 byte입니다. STRUCT_B의 크기는 16 byte입니다.
멤버 중 가장 큰 크기로 할당된다면 STRUCT_B의 크기는 24 byte가 되어야 할 것입니다.
다음 전처리기를 이용하여 특정 바이트를 기준으로 정렬되도록 변경할 수 있습니다. 일반적인 Applicaion을 만드는 경우라면 필요 없지만, 매우 한정적인 메모리를 가지는 임베디드 시스템을 만드는 경우에는 유용할 수 있습니다.
#pragma pack(Bytes)