기본 패턴
c
#include <stddef.h> // offsetof
#include <stdio.h>
struct Padded {
char a; // 1바이트
int b; // 4바이트 — 4배수 정렬 위해 3바이트 패딩 삽입
char c; // 1바이트 — 뒤에 3바이트 패딩 삽입
};
printf("%zu\n", sizeof(struct Padded)); // 12 (1+3+4+1+3)
printf("%zu\n", offsetof(struct Padded, b)); // 4
printf("%zu\n", offsetof(struct Padded, c)); // 8설명
정렬이 왜 필요한가
CPU는 메모리를 타입 크기의 배수 주소에서 읽을 때 가장 효율적입니다. int(4바이트)는 주소가 4의 배수여야 한 번의 메모리 접근으로 읽힙니다. 정렬되지 않으면 두 번 읽어야 하거나 하드웨어 예외가 발생합니다. 컴파일러는 이 규칙을 만족하기 위해 패딩 바이트를 자동으로 삽입합니다.
text
struct Padded 메모리 레이아웃 (12바이트):
[a:1][pad:3][ b:4 ][c:1][pad:3]
0 1 4 8 9각 멤버의 정렬 요구:
char: 1바이트 정렬 (어느 주소나 가능)short: 2바이트 정렬int,float: 4바이트 정렬double, 포인터: 8바이트 정렬 (64비트)
offsetof — 실제 오프셋 확인
offsetof(type, member)는 구조체 시작부터 멤버까지의 실제 바이트 오프셋을 컴파일 타임에 반환합니다. 네트워크 프로토콜이나 파일 포맷에서 직렬화할 때 필수입니다.
c
struct Point3D {
float x; // offset 0
float y; // offset 4
float z; // offset 8
};
struct Mixed {
char flag; // offset 0
double value; // offset 8 (7바이트 패딩)
int count; // offset 16
}; // 총 24바이트 (마지막에 4바이트 패딩)
printf("%zu\n", offsetof(struct Mixed, value)); // 8
printf("%zu\n", offsetof(struct Mixed, count)); // 16
printf("%zu\n", sizeof(struct Mixed)); // 24필드 순서를 바꿔 패딩 최소화
크기가 큰 멤버부터 내림차순으로 배치하면 패딩이 줄어듭니다.
c
// ❌ 비효율적 순서 — 12바이트
struct Bad {
char a; // 1 + 3(패딩)
int b; // 4
char c; // 1 + 3(패딩)
};
// ✅ 큰 것부터 — 8바이트 (패딩 0)
struct Good {
int b; // 4
char a; // 1
char c; // 1
// 2바이트 패딩 (구조체 배열 정렬용)
};
printf("%zu vs %zu\n",
sizeof(struct Bad), // 12
sizeof(struct Good)); // 8#pragma pack — 패딩 강제 제거
네트워크 패킷이나 파일 포맷처럼 바이트 정확성이 필요한 경우 패딩을 제거합니다. 하지만 성능 저하와 이식성 문제가 있으므로 꼭 필요한 경우에만 씁니다.
c
#pragma pack(push, 1) // 1바이트 정렬 강제
struct PacketHeader {
uint8_t type; // 1
uint16_t length; // 2 (정렬 안 됨)
uint32_t checksum; // 4 (정렬 안 됨)
};
#pragma pack(pop)
printf("%zu\n", sizeof(struct PacketHeader)); // 7 (패딩 없음)빠른 정리
| 상황 | 적합한 선택 |
|---|---|
| 구조체 실제 크기 확인 | sizeof(struct T) |
| 멤버 실제 오프셋 확인 | offsetof(struct T, member) |
| 구조체 크기 최소화 | 큰 멤버부터 내림차순 배치 |
| 네트워크/파일 포맷 (패딩 금지) | #pragma pack(1) 또는 __attribute__((packed)) |
| 정렬 요구 확인 | _Alignof(T) (C11) |
주의할 점
sizeof(struct)가 멤버 크기의 합과 다를 수 있습니다. 직렬화 시 구조체를 통째로 복사하면 패딩 바이트도 함께 전송됩니다.
c
struct Header {
char type; // 1
int length; // 4 — 앞에 3바이트 패딩
};
// ❌ 8바이트 전체 전송 — 3바이트 쓰레기 패딩 포함
write(fd, &hdr, sizeof(struct Header));
// ✅ 필드를 개별적으로 직렬화
uint8_t buf[5];
buf[0] = hdr.type;
memcpy(buf + 1, &hdr.length, sizeof(hdr.length));
write(fd, buf, sizeof(buf));#pragma pack으로 패딩을 제거하면 비정렬 접근으로 인한 성능 저하 또는 일부 아키텍처에서 버스 오류가 발생할 수 있습니다.
참고 링크
1 sources