구조체 정렬·패딩 계산기 쉽게 이해하기 — 개념과 원리
C나 C++ 컴파일러는 구조체의 각 멤버가 자료형에 맞는 주소에서 시작하도록, 멤버 사이에 쓰지 않는 패딩 바이트를 넣는 경우가 많습니다. 이 계산기는 입력한 순서대로 멤버를 배치해 각 오프셋, 구조체 전체 크기, 패딩 크기를 보여 주고, 큰 멤버부터 정렬했을 때의 크기와 비교합니다. 자료구조나 이진 파일 형식, 통신 메시지를 설계하거나 큰 배열의 메모리를 줄일 때 유용합니다.
계산 방법
각 멤버의 정렬 단위가 자신의 크기와 같다는 자연 정렬을 가정합니다.
- 오프셋 0에서 시작해, 크기가 s인 멤버마다 현재 오프셋을 s의 배수로 올림합니다. 오프셋 = ⌈오프셋 ÷ s⌉ × s
- 그 위치에 멤버를 두고 오프셋을 s만큼 늘립니다.
- 마지막 멤버 뒤의 끝 위치를 가장 큰 멤버 크기의 배수로 올림한 값이 전체 크기입니다.
- 패딩 = 전체 크기 − 멤버 크기의 합
- 정렬 후 크기: 큰 멤버부터 작은 멤버 순으로 같은 계산을 반복한 값
원리
프로세서는 n바이트 값이 n의 배수 주소에 있을 때 가장 효율적으로 읽습니다. 정렬이 맞지 않으면 느려지거나 아예 오류가 나는 구조도 있어서, 컴파일러는 플랫폼 ABI에 따라 패딩을 넣습니다. 끝에 붙는 패딩은 구조체가 배열로 연달아 놓일 때 다음 원소의 멤버도 정렬되도록 하기 위한 것입니다. 큰 멤버부터 배치하면 작은 멤버가 빈틈을 메워 낭비가 줄어듭니다.
예제 풀이
- 멤버 순서: 1, 4, 2, 8, 1바이트(데이터 합 16바이트)
- 1바이트 → 0번지, 다음 위치 1
- 4바이트 → 1을 4로 올림해 4번지, 다음 8
- 2바이트 → 8번지, 다음 10. 8바이트 → 10을 16으로 올림해 16번지, 다음 24
- 1바이트 → 24번지, 끝 25. 8의 배수로 올림하면 전체 크기 32
- 패딩: 32 − 16 = 16바이트. 8, 4, 2, 1, 1 순서로 놓으면 오프셋 0, 8, 12, 14, 15로 16바이트가 됩니다.
결과 해석
이 예에서는 구조체의 절반이 패딩이며, 순서만 바꿔도 크기가 반으로 줄어듭니다. 이런 구조체 100만 개짜리 배열이라면 16 MB를 아낄 수 있습니다. 패딩이 0이면 이미 빈틈없는 배치입니다.
흔한 실수
- 구조체 크기가 멤버 크기의 합과 같다고 생각하는 경우
- 구조체 끝의 패딩을 빼먹는 경우
- char[16] 같은 배열이 16바이트 정렬이 필요하다고 보는 경우. 배열은 원소 자료형에 맞춰 정렬되며, 이 단순 모델은 이를 반영하지 않습니다.
자주 묻는 질문
C 표준이 패딩을 강제하나요?
멤버 뒤의 패딩은 구현에 맡기지만, 첫 멤버 앞에는 패딩을 둘 수 없어 오프셋은 항상 0입니다.
항상 pack으로 압축하면 안 되나요?
정렬이 어긋난 접근은 느리거나 오류를 낼 수 있어, 정확한 이진 형식이 필요할 때만 쓰는 것이 좋습니다.
멤버 순서를 바꾸면 동작이 달라지나요?
논리적으로는 같지만 이진 배치가 바뀌므로 기존 파일이나 프로토콜과의 호환성이 깨질 수 있습니다.
출처: ISO/IEC 9899 (C 표준), 6.7.2.1 Structure and union specifiers · System V Application Binary Interface, AMD64 Architecture Processor Supplement