57. 객체 정렬(alignment): alignof/alignas·패딩·over-aligned·new 정렬 보장
난이도 상내 리뷰 · C++
해설 · C++
해설 — 객체 정렬(alignment): alignof/alignas·패딩·over-aligned·new 정렬 보장
난이도: 상
요약
정렬 요구는 각 타입이 놓일 수 있는 주소를 제한한다. 구조체는 멤버 정렬을 만족시키려 패딩을 넣고, 전체 크기는 가장 큰 멤버 정렬의 배수로 반올림된다. alignas 로 over-align 하면 크기·정렬이 커지고, C++17 부터 new 는 over-aligned 타입의 정렬을 보장한다.
예측 출력 (x86-64 Linux, g++)
1 4 8
12 4
16 16
16 8
4 8
aligned16
단계별 내부 동작
alignof(char)=1, alignof(int)=4, alignof(double)=8. 각 타입은 자기 크기(스칼라)만큼 정렬되는 것이 일반적이다.struct A { char c; int i; char d; }→ 레이아웃:c@0 (1B)- 패딩 @1..3 (int 정렬 4 맞추기)
i@4..7d@8 (1B)- 꼬리 패딩 @9..11 (전체 크기를 alignof(A)=4 의 배수로)
sizeof(A)=12,alignof(A)=4. → 출력12 4.
struct alignas(16) Over { int x; }→ 정렬 16 강제. 크기도 정렬 배수라야 하므로sizeof=16,alignof=16. →16 16.struct Packed { char c; double d; }→c@0, 패딩@1..7,d@8..15.sizeof=16,alignof=8(가장 큰 멤버 double). →16 8.offsetof(A,i)=4,offsetof(A,d)=8. 위 레이아웃과 일치.new Over()→ C++17 부터 over-aligned 타입은 정렬-인지 operator new(operator new(size_t, align_val_t))로 할당되어 16바이트 정렬이 보장된다.주소 % 16 == 0→aligned16.
흔한 오해·함정
- 멤버 순서로 크기가 달라진다:
A를{ int i; char c; char d; }로 재배열하면i@0..3,c@4,d@5, 꼬리 패딩@6..7 →sizeof=8. 큰 멤버부터 배치하면 패딩이 준다(구조체 팩킹 최적화). sizeof는 배열 스트라이드: 꼬리 패딩이 있는 이유는 배열 원소 간 정렬을 유지하기 위해서다.A a[2]에서a[1]도 4바이트 정렬돼야 하므로sizeof(A)가 정렬 배수여야 한다.- C++11/14 의 new 는 over-align 미보장: C++17 이전에는
new Over()가 16바이트 정렬을 보장하지 않아alignas(64)SPSC 큐 노드 등이 잘못 정렬될 수 있었다. C++17 정렬-인지 new, 또는std::aligned_alloc/posix_memalign이 필요했다. - std::atomic 과 정렬: 원자 연산의 lock-free 보장과 성능은 대상이 자연 정렬돼 있을 때 성립한다. 잘못 정렬된(packed) 원자 변수는 성능 저하 또는 플랫폼에 따라 오작동한다.
면접 포인트
- 정렬이 중요한 3대 이유: (1) 하드웨어가 정렬 안 된 접근을 느리게/불가하게 처리, (2)
false sharing회피를 위해 캐시라인(보통 64B)으로 패딩·정렬, (3) 바이너리 직렬화에서 구조체를 그대로 memcpy 하면 패딩·정렬·엔디안 가정이 이식성을 깨뜨림. - 구조체 크기 계산: 멤버를 순서대로 각 멤버 정렬로 올림 배치 → 마지막에 전체를 최대 정렬로 올림. 멤버 재정렬로 패딩을 줄이는 것이 캐시·메모리 최적화의 기본기.
- over-aligned 타입(예:
alignas(64))은 C++17 정렬-인지 new 로 힙에서도 정렬이 보장된다.