C++에서 구조체의 멤버 맞춤(alignment)은 메모리 효율성과 접근 속도를 최적화하기 위해 구조체 내 멤버들이 메모리에 배치되는 방식을 말합니다.
이는 CPU가 메모리를 효율적으로 접근하기 위해 특정 크기 단위로 데이터를 배치하는 것을 요구하기 때문입니다.
기본 원칙은 구조체의 멤버 중 가장 큰 자료형의 정렬 크기(alignment requirement)에 맞춰 배치된다는 것인데, 이를 통해 CPU가 메모리 접근 시 잘못된 정렬(unaligned access)로 인해 발생하는 성능 저하를 방지합니다.
기본 규칙
• 멤버 변수는 자신의 크기 또는 구조체의 정렬 크기 중 작은 값에 맞춰 배치됩니다.
• 구조체의 크기는 전체 구조체의 정렬 크기의 배수가 됩니다.
패딩(padding)
멤버 변수 사이에 삽입되는 패딩은 멤버의 주소가 정렬 요구 사항을 충족하도록 하기 위해 삽입됩니다.
#include <iostream>
#include <cstddef>
struct Example {
char a; // 1바이트
int b; // 4바이트
char c; // 1바이트
};
int main() {
std::cout << "Size of Example: " << sizeof(Example) << std::endl;
std::cout << "Offset of a: " << offsetof(Example, a) << std::endl;
std::cout << "Offset of b: " << offsetof(Example, b) << std::endl;
std::cout << "Offset of c: " << offsetof(Example, c) << std::endl;
return 0;
}
Size of Example: 12
Offset of a: 0
Offset of b: 4
Offset of c: 8
1. char a는 1바이트이고, 정렬 요구 사항은 1바이트입니다.
2. int b는 4바이트이고, 정렬 요구 사항은 4바이트이므로 b의 주소는 4바이트 경계에서 시작해야 합니다. 따라서 a와 b 사이에 3바이트의 패딩이 삽입됩니다.
3. char c는 1바이트이고, 정렬 요구 사항은 1바이트입니다. 하지만 구조체의 정렬 크기는 4바이트 단위이므로 끝에 3바이트의 패딩이 추가되어 총 크기가 12바이트가 됩니다.
패딩 최소화 방법
구조체의 크기를 줄이기 위해 패딩을 최소화하려면 멤버 변수를 정렬 크기가 큰 순서대로 선언하는 것이 좋습니다.
구조체의 전체 크기는 가장 큰 멤버의 정렬 요구사항의 배수가 되기 때문입니다.
먼저, 패딩 최소화를 고려하지 않은 구조체 선언입니다.
struct NonOptimized {
char a; // 1바이트
int b; // 4바이트
char c; // 1바이트
};
a: 0x00 (1바이트)
[패딩]: 0x01, 0x02, 0x03 (3바이트)
b: 0x04 (4바이트)
c: 0x08 (1바이트)
[패딩]: 0x09, 0x0A, 0x0B (3바이트)
=> 총 크기 12바이트 중 패딩 6바이트
패딩 최소화를 고려한 구조체 선언입니다.
struct Optimized {
int b; // 4바이트
char a; // 1바이트
char c; // 1바이트
};
b: 0x00 (4바이트)
a: 0x04 (1바이트)
c: 0x05 (1바이트)
[패딩]: 0x06, 0x07 (2바이트)
=> 총 크기 8바이트 중 패딩 2바이트
위의 구조체에 적용되는 메모리 정렬 및 패딩 관련 개념은 클래스의 경우에도 적용됩니다.
다만, 클래스는 구조체와 달리 상속이 가능하고, 가상 함수가 포함될 수 있습니다.
먼저, 상속의 경우입니다.
class Base {
char a; // 1바이트
};
class Derived : public Base {
int b; // 4바이트
};
=>
a: 0x00 (1바이트, Base 클래스)
[패딩]: 0x01, 0x02, 0x03 (3바이트)
b: 0x04 (4바이트, Derived 클래스)
기본 클래스의 멤버 -> 파생 클래스의 멤버 순서대로 메모리에 연속적으로 배치되기 때문에 이 과정에서 패딩이 추가될 수 있습니다.
가상함수의 경우 가상함수테이블에 대한 포인터가 추가됩니다. 이 포인터는 일반적으로 4바이트 또는 8바이트의 크기를 가지며, 클래스의 크기에 영향을 미치게 됩니다.
결론
클래스에서도 구조체와 동일한 방식으로 패딩과 정렬이 적용되며, 패딩 최소화 전략도 똑같이 사용할 수 있습니다. 하지만 클래스의 추가적인 요소(가상 함수, 상속 등)는 구조체보다 정렬과 크기 계산을 복잡하게 만들 수 있습니다. 패딩 최소화는 클래스 설계에서 중요한 최적화 기술이지만, 코드의 가독성과 유지보수성도 고려해야 합니다.
'C++' 카테고리의 다른 글
[C++]Linux - 스레드 동기화와 뮤텍스(Mutex) (1) | 2024.12.27 |
---|---|
널(null) 포인터 nullptr에 대해 (0) | 2024.12.23 |
const, constexpr, consteval에 대해 (0) | 2024.12.23 |