Skip to main content

[C++] 바이트 정렬과 비트 필드



바이트 정렬 #

  • 구조체나 클래스 안의 멤버 변수들은 가장 큰 크기의 멤버 변수를 기준으로 정렬되어 메모리에 저장된다.
  • 왜 그럴까?
    • CPU가 RAM에 접근하는 횟수를 줄이기 위해서다.
    • 32bit 프로세서 컴퓨터가 한번에 가져올 수 있는 데이터 단위는 32bit다.
    • 만약 데이터가 32bit 보다 작은데도 불구하고 두 단위에 걸쳐서 저장되어 있다면 두 번의 접근과 추가적인 연산이 필요해진다.
    • 따라서 컴파일러는 패딩(Padding)을 추가해서 바이트 정렬을 수행한다.

  • 멤버 변수들을 잘 정렬하면, 프로그램의 메모리 사용 효율을 높일 수 있다.
// 가장 큰 크기의 멤버 변수가 double이므로 8 byte 단위로 나뉘어 저장된다. 

struct S
{
    char b1;   // 1 byte -> 처음 8 byte의 앞부분에 저장된다. 
    double b2; // 8 byte -> 처음 8 byte에 모두 못 담기므로, 두 번째 8 byte에 저장된다. 
    int b3;    // 4 byte -> 세 번째 8 byte에 저장된다. 
        
    // ==> 총 24 byte
};

struct SS
{
    // (두 번째, 세 번째 멤버 변수의 위치를 바꾸었다.)
    
    char b1;   // 1 byte -> 처음 8 byte의 앞부분에 저장된다. 
    int b3;    // 4 byte -> 처음 8 byte의 뒷부분에 저장된다. 
    double b2; // 8 byte -> 두 번째 8 byte에 저장된다. 
        
    // ==> 총 16 byte
};



비트 필트 #

  • 멤버 변수의 사이즈를 비트 단위로 명시하는 것이다.
  • 비트 필드의 각 멤버는 최하위 비트(Least Significant Bit, LSB)부터 차례대로 배치된다.
  • 비트 필드의 범위를 넘어서는 값은 저장하지 않는다.
  • 원래 타입의 크기를 넘어서는 비트 필드를 사용하면 데이터는 타입 크기에 제한되고, 나머지는 패딩이 들어간다.
#include <iostream>

struct S
{
    // 총 크기는 4 byte
    unsigned int b : 3; // 3 bit 사이즈이므로 0 ~ 7 값만 저장할 수 있다.
};

int main()
{
    S s = {6};
 
    ++s.b;
    std::cout << s.b << '\n'; // 7 
 
    ++s.b; 
    std::cout << s.b << '\n'; // 8은 범위를 넘어서므로, 0이 된다. 
}

  • 바이트 정렬과 함께보는 비트 필드
#include <iostream>
#include <cstdint>
#include <bit>
 
struct S
{
    // 총 크기는 2 byte
    unsigned char b1 : 3;  // 3 bits
    unsigned char    : 2;  // 2 bits -> 사용하지 않는다. 
    unsigned char b2 : 6;  // 6 bits -> 처음 byte에 모두 들어갈 수 없으므로 두 번째 byte에 저장된다. 
    unsigned char b3 : 2;  // 2 bits
};
 
int main()
{
    std::cout << sizeof(S) << '\n';
 
    S s;
    s.b1 = 0b111;
    s.b2 = 0b101111;
    s.b3 = 0b11;
 
    auto i = std::bit_cast<std::uint16_t>(s);
    // usually prints 1110000011110111
    // breakdown is:  \_/\/\_/\____/\/
    //                 b1 u a   b2  b3
    // u -> 사용하지 않는 부분
    // a -> 컴파일러가 바이트 정렬을 위해 패딩을 넣은 부분 
    
    for (auto b = i; b; b >>= 1)  // print LSB-first
        std::cout << (b & 1);
    std::cout << '\n';
}



References #



Pero
Author
Pero
Keep moving forward.

comments powered by Disqus