CodeNote
Coding Note
CodeNote
전체 방문자
오늘
어제
  • 전체 보기 (35)
    • C++ (33)
      • Modern C++ (12)
      • Modern C++ STL (0)
      • Thread (16)
      • Thread (Async) (5)
    • 디자인패턴 (0)
    • Algorithm (2)
    • Electron (0)
    • Python (0)

블로그 메뉴

  • 홈
  • Github
  • 태그
  • 방명록

공지사항

인기 글

태그

  • mutex
  • LOCK
  • C++ #Memory
  • Free
  • 자료구조
  • C++

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
CodeNote

Coding Note

[C++] Union
C++/Modern C++

[C++] Union

2022. 11. 4. 09:19

union

잘 쓰이진 않는 union의 가장 큰 목적은 Memory Saving(메모리 절약)을 하는 것이다. 요새 임베디드 기기는 메모리가 크게 설정되지만 이 중에서도 특별한 하드웨어에서는 몇 KB만 가능한 케이스들이 있다. 이런 특수한 경우에 메모리를 절약하기 위해 사용된다.

 

Example Code : struct와 union 비교

struct S // 4 + (4) + 8 = 16 byte
{
    int i;	  // 4 byte
    double d; // 8 Byte
};
uㅎnion U // 8 byte
{
    int i;    // 4 byte
    double d; // 8 byte
};

그림을 통해 어떻게 S는 16byte가 나오고 U는 8byte가 나오는지 확인해본다.

 

struct와 union 메모리 형태

 

union 같은 경우 하나의 공간이 있으면 int 또는 double이 들어간다. 즉, 한 번에 하나의 타입만 들어가서 사용할 수 있다는 것이다.

 

Example Code

U u;
u.i = 10;
std::cout << u.i << std::endl; // 10

u.d = 0.3; // 10에 대한 정보가 없어지고 double에 0.3이 들어간다.
std::cout << u.d << std::endl; // 0.3

// 0.3 double 값이 들어가있는 상황에서 int 타입에 접근한다면?
std::cout << u.i << std::endl; // 이상한 값이 나온다.

u.d에 double 값을 넣은 상태에서 u.i가  이상한 값이 나오는 이유는 현재 double에 0.3 값이 들어있는 상황에서 비트로 나타내게 되면 0001 .................. 이런식으로 들어있을 것이다. u.i를 호출하면 int 부분만큼(4Byte)만 비트를 가져와서 int 타입으로 Decoding을 하여 출력하게 되면서 이상한 값이 출력되는 것이다.


이처럼 double 써놓고 int로 쓰는 행위는 undefined behavior이다.

그만큼 union은 조심히 사용해야한다.

 

조금 더 복잡한 타입에 대한 내용을 보면,

Example Code : heap 데이터 사용

union SV
{
    std::string str;
    std::vector<int> vec;
    ~SV() {}
};

SV s = { "Hello, world" };
std::cout << "s.str = " << s.str << '\n';
// string을 vector로 바꾸기 위해 개발자가 직접 string의 destructor를 콜한다.
s.str.~basic_string(); 
// 이후 vector의 constructor를 호출한다.
new (&s.vec) std::vector<int>; 
// 그리고나서야 union을 vector로 사용할 수 있게된다.
s.vec.push_back(10); 
std::cout << s.vec.size() << '\n';
s.vec.~vector(); // 마지막에 종료가 되기전에 직접 destructor를 콜해야한다.

cppreference의 예제인데 union에서 heap 공간을 사용하는 string과 vector를 가지고 있는 경우다. 타입을 바꿀 때마다 직접적으로 constructor와 destructor를 호출해줘야한다.

 

이처럼 코드를 짜다가 개발자가 만약 vector의 constructor를 빼먹는다면 segmentaion fault 에러가 발생하면서 프로세스가 죽는다. union은 개발자가 신경써야하는 부분이 많기 때문에 조심해서 사용해야한다.

 

이를 어느 정도 안전하게 사용할 수 있는 팁이 있다. 바로 현재 union이 어떠한 타입이 들어있는지를 추적하는 것이다.

 

Example Code

// union-like classes / tagged union
struct S
{
    enum { CHAR, INT, DOUBLE } tag;
    union
    {
        char c;
        int i;
        double d;
    };
};

바로 저 tag에 현재 타입 중 어떤 값을 넣고 있는지 추적하기 때문에 훨씬 더 안전하게 사용할 수 있게 된다. 그럼에도 불구하고 union 타입은 여전히 위험하다. 

 

이를 훨씬 더 편하고 안전하게 만들 수 있는 클래스 템플릿이 있는데 C++17 부터 추가된 std::variant이다. std::variant를 사용하게되면 다른 타입의 값을 넣어도 직접적으로 contructor와 destructor를 호출할 필요도 없고 undefined behavior를 원천적으로 막아준다.

'C++ > Modern C++' 카테고리의 다른 글

[C++] Type Punning (타입 장난)  (0) 2022.11.10
[C++17] std::variant  (0) 2022.11.07
[C++17] std::optional  (0) 2022.10.31
[C++] 가상함수 원리  (0) 2022.10.24
[C++] attributes(속성)  (0) 2022.10.13
    'C++/Modern C++' 카테고리의 다른 글
    • [C++] Type Punning (타입 장난)
    • [C++17] std::variant
    • [C++17] std::optional
    • [C++] 가상함수 원리
    CodeNote
    CodeNote
    기록 블로그

    티스토리툴바