언제 노조를 사용하겠습니까? C- 전용 날의 잔재입니까?


133

나는 배웠지 만 실제로 노동 조합을 얻지는 못한다. 내가 통과하는 모든 C 또는 C ++ 텍스트는 그것들을 소개하지만 (때로는 통과 할 때도 있지만) 왜 또는 어디에서 사용해야하는지에 대한 실제적인 예는 거의 없습니다. 현대 (또는 레거시) 사례에서 노동 조합은 언제 유용할까요? 내 두 가지 추측은 작업 할 공간이 매우 제한적이거나 API (또는 비슷한 것)를 개발할 때 최종 사용자가 여러 객체 / 유형의 인스턴스를 하나만 갖도록하려는 경우 마이크로 프로세서를 프로그래밍하는 것입니다. 한 번. 이 두 가지 추측이 오른쪽에 가깝습니까?


31
C / C ++는 언어가 아닙니다. 공용체는 C에서는 적당히 유용하고 C ++에서는 거의 쓸모가 없습니다. C ++에서 "C를 기반으로하는 C ++의 잔존물"이라고 말하는 것이 맞지만 C ++이 C를 대신하는 것처럼 "C 일의 잔재"라고 말하는 것은 아닙니다.
R .. GitHub ICE HELPING ICE

12
C ++의 조합을 대체하는 것이 무엇인지 또는 왜 C ++에서 쓸모가 없는지 자세히 설명 할 수 있습니까?
Russel

3
C ++의 공용체 대신 클래스와 상속이 사용됩니다. C의 공용체는 형식이 안전한 다형성에 거의 독점적으로 사용됩니다. 수업이 훨씬 나아졌습니다. (C-스타일의 다형성에 대한 참조 vz0의 대답은)
tobyodavies

6
@R .. : C ++에서 공용체는 여전히 적당히 유용합니다. 아래 답변을 참조하십시오.
Michael

2
조합은 운영 체제의 장 또는 예를 들어 사운드 파일을 조립 / 분해하는 패키지에서 매우 유용 할 수 있습니다. 이러한 맥락에서 데이터 / 엔디안 변환, 저수준 다형성 등 여러 가지 방식으로 사용됩니다. 그렇습니다. 동일한 문제에 대한 다른 해결책이 있지만 (주로 포인터 유형간에 캐스트) 공용체는 종종 더 깨끗하고 더 나은 자체 문서화입니다.
핫 릭

답변:


105

노동 조합은 일반적으로 차별 자의 회사와 함께 사용됩니다. 노동 조합의 어느 분야가 유효한지를 나타내는 변수입니다. 예를 들어, 고유 한 변형 유형 을 생성한다고 가정 해 보겠습니다 .

struct my_variant_t {
    int type;
    union {
        char char_value;
        short short_value;
        int int_value;
        long long_value;
        float float_value;
        double double_value;
        void* ptr_value;
    };
};

그런 다음 다음과 같이 사용하십시오.

/* construct a new float variant instance */
void init_float(struct my_variant_t* v, float initial_value) {
    v->type = VAR_FLOAT;
    v->float_value = initial_value;
}

/* Increments the value of the variant by the given int */
void inc_variant_by_int(struct my_variant_t* v, int n) {
    switch (v->type) {
    case VAR_FLOAT:
        v->float_value += n;
        break;

    case VAR_INT:
        v->int_value += n;
        break;
    ...
    }
}

이것은 실제로 Visual Basic 내부에서 매우 일반적인 관용구입니다.

실제 예는 SDL의 SDL_Event union을 참조하십시오 . ( 실제 소스 코드는 여기 ). 거기에있다 type노조의 상단에있는 필드와 같은 필드는 모든 SDL_ * 이벤트 구조체에 반복됩니다. 그런 다음 올바른 이벤트를 처리하려면 type필드 값을 확인해야 합니다.

장점은 간단합니다. 불필요한 메모리를 사용하지 않고 모든 이벤트 유형을 처리 할 수있는 단일 데이터 유형이 있습니다.


2
큰! 이 경우 Sdl 함수가 클래스 계층 구조로 구현되지 않은 이유가 궁금합니다. C ++뿐만 아니라 C 호환 가능하게 만드는 것입니까?
Russel

12
@Russel C ++ 클래스는 C 프로그램에서 사용할 수 없지만 'extern "C"블록을 사용하여 C ++에서 C 구조체 / 유니언을 쉽게 액세스 할 수 있습니다.
vz0

1
이 변형 패턴은 종종 예를 들어, 정의, 프로그래밍 언어 통역에 사용됩니다 struct object에서 github.com/petermichaux/bootstrap-scheme/blob/v0.21/scheme.c
아담 로젠

1
멋진 설명입니다. 나는 항상 노조가 무엇인지 알았지 만, 왜 누군가가 노조를 사용할 수 있을지에 대한 실제 이유를 보지 못했습니다.
riwalk

@ Stargazer712, Google 코드 검색 : google.com/…
kagali-san

87

C ++ 공용체가 꽤 멋지다는 것을 알았습니다. 사람들은 일반적으로 "제자리에서"통합 인스턴스의 값을 변경하려는 유스 케이스 만 생각하는 것 같습니다 (메모리를 절약하거나 의심스러운 변환을 수행하는 데만 도움이 됨).

실제로 Union 인스턴스의 가치를 절대로 변경하지 않더라도 Union 은 소프트웨어 엔지니어링 도구로서 큰 힘을 발휘할 수 있습니다 .

사용 사례 1 : 카멜레온

공용체를 사용하면 하나의 명칭으로 여러 개의 임의 클래스를 다시 그룹화 할 수 있습니다. 기본 클래스 및 파생 클래스의 경우와 유사하지 않습니다. 그러나 변경 사항은 주어진 통합 인스턴스로 할 수 있고 할 수없는 것입니다.

struct Batman;
struct BaseballBat;

union Bat
{
    Batman brucewayne;
    BaseballBat club;
};

ReturnType1 f(void)
{
    BaseballBat bb = {/* */};
    Bat b;
    b.club = bb;
    // do something with b.club
}

ReturnType2 g(Bat& b)
{
    // do something with b, but how do we know what's inside?
}

Bat returnsBat(void);
ReturnType3 h(void)
{
    Bat b = returnsBat();
    // do something with b, but how do we know what's inside?
}

프로그래머는 주어진 유니온 인스턴스의 컨텐츠 유형을 사용하고자 할 때 특정 유형이어야합니다. f위의 기능에 해당 합니다. 그러나 함수가 g위 의 경우와 같이 전달 된 인수로 통합 인스턴스를 수신하면 함수와 함께 무엇을 해야할지 알 수 없습니다. 공용체 인스턴스를 반환하는 함수에도 동일하게 적용됩니다.h . 호출자는 내부 내용을 어떻게 알 수 있습니까?

통합 인스턴스가 인수 또는 반환 값으로 전달되지 않으면 프로그래머가 내용을 변경하기로 선택했을 때 매우 급격한 삶을 살 수밖에 없습니다.

Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;

그리고 이것이 가장 인기있는 노동 조합의 사용 사례입니다. 또 다른 유스 케이스는 통합 인스턴스가 유형을 알려주는 무언가와 함께 제공되는 경우입니다.

사용 사례 2 : "니스 난, 당신을 만나서 object에서,Class "

프로그래머가 유니언 인스턴스를 항상 타입 디스크립터와 짝짓기로 선택했다고 가정 해 보자 (그러한 객체에 대한 구현을 상상하기 위해 독자의 재량에 맡기겠다). 이것은 프로그래머가 원하는 것이 메모리를 절약하고 타입 디스크립터의 크기가 유니온의 크기와 관련하여 무시할 수없는 경우 유니온 자체의 목적을 무효화합니다. 그러나 통합 인스턴스가 내부에 무엇이 있는지 모르는 수신자 또는 호출자에게 인수 또는 반환 값으로 전달 될 수 있어야한다고 가정 해 봅시다.

그런 다음 프로그래머는 switch Bruce Wayne에게 나무 막대기 또는 이와 동등한 것을 구별하기 위해 제어 흐름 설명을 작성해야합니다. 노조에 두 가지 유형의 내용 만있을 때 그리 나쁘지는 않지만 분명히 노조는 더 이상 확장되지 않습니다.

사용 사례 3 :

ISO C ++ 표준대한 권장 사항 작성자가 2008 년에 다시 제안한 것처럼,

많은 중요한 문제 도메인에는 많은 수의 객체 또는 제한된 메모리 리소스가 필요합니다. 이러한 상황에서 공간 보존은 매우 중요하며, 노조가 종종이를 수행하는 완벽한 방법입니다. 실제로 일반적인 유스 케이스는 노조가 수명 동안 활성 멤버를 변경하지 않는 상황입니다. 마치 하나의 멤버 만 포함 된 구조체 인 것처럼 구성, 복사 및 제거 할 수 있습니다. 이것의 전형적인 적용은 동적으로 할당되지 않는 이종 유형의 관련되지 않은 유형의 콜렉션을 작성하는 것입니다 (아마도 이들은 맵 또는 배열의 멤버로 제자리에 구성되어 있음).

이제 UML 클래스 다이어그램을 사용한 예제가 있습니다.

클래스 A에 대한 많은 구성

일반 영어로 된 상황 : 클래스 A의 객체는 B1, ..., Bn 사이의 모든 클래스의 객체를 가질 있으며 각 유형 중 최대 하나는 n 이 10보다 큰 숫자입니다.

다음과 같이 필드 (데이터 멤버)를 A에 추가하고 싶지 않습니다.

private:
    B1 b1;
    .
    .
    .
    Bn bn;

n 이 다를 수 있기 때문에 (믹스에 Bx 클래스를 추가하고 싶을 수도 있음) 생성자와 혼동을 일으키고 A 객체가 많은 공간을 차지하기 때문입니다.

우리는 캐스트 void*가있는 Bx객체 에 대한 포인터 의 이상한 컨테이너를 사용 하여 객체를 검색 할 수는 있지만 모호하고 C 스타일입니다 ... 그러나 더 중요한 것은 동적으로 할당 된 많은 객체의 수명을 관리 할 수있게 해줍니다.

대신 수행 할 수있는 작업은 다음과 같습니다.

union Bee
{
    B1 b1;
    .
    .
    .
    Bn bn;
};

enum BeesTypes { TYPE_B1, ..., TYPE_BN };

class A
{
private:
    std::unordered_map<int, Bee> data; // C++11, otherwise use std::map

public:
    Bee get(int); // the implementation is obvious: get from the unordered map
};

그런 다음에서 노조 인스턴스의 컨텐츠를 얻기 위해 data당신이 사용 a.get(TYPE_B2).b2하고 좋아하는, a클래스입니다 A인스턴스를.

C ++ 11에서는 공용체가 제한되지 않기 때문에 이것이 더 강력합니다. 자세한 내용 은 위의 문서 또는 이 기사연결된 문서 를 참조하십시오.


이것은 매우 도움이되었고 두 번째 기사의 시리즈는 매우 유익했습니다. 감사.
앤드류

38

하나의 예는 임베디드 영역에 있으며, 레지스터의 각 비트는 다른 의미를 가질 수 있습니다. 예를 들어, 8 비트 정수와 8 개의 개별 1 비트 비트 필드가있는 구조체를 결합하면 하나의 비트 또는 전체 바이트를 변경할 수 있습니다.


7
이것은 장치 드라이버에서도 매우 일반적입니다. 몇 년 전 저는 프로젝트에 이와 같은 조합을 사용하여 많은 코드를 작성했습니다 . 일반적으로 권장되지 않으며 경우에 따라 컴파일러마다 다를 수 있지만 작동합니다.
thkala

11
나는 "권장하지 않음"이라고 부르지 않을 것입니다. 임베드 된 공간에서는 대체품보다 훨씬 깨끗하고 오류가 적습니다. 일반적으로 많은 명시 적 캐스트와 void*마스크 또는 시프트가 필요합니다.
bta

응? 노골적인 캐스트가 많습니까? 같은 간단한 문장 날 것으로 보인다 REG |= MASKREG &= ~MASK. 오류가 발생하기 쉬운 경우 #define SETBITS(reg, mask)및에 넣으십시오 #define CLRBITS(reg, mask). 특정 순서로 비트를 얻기 위해 컴파일러에 의존하지 마십시오 ( stackoverflow.com/questions/1490092/… )
Michael

26

Herb Sutter 는 약 6 년 전에 GOTW 에 다음과 같이 강조 했습니다.

"그러나 노조는 이전 시대에 비해 오래 지속 된 것이라고 생각하지 않습니다. 노조는 아마도 데이터가 겹치도록하여 공간을 절약하는 데 가장 유용 할 것입니다. 이것은 C ++ 과 오늘날의 현대 세계에서 여전히 바람직 합니다. 고급 C ++세계 표준 라이브러리 구현은 이제 "작은 문자열 최적화"를 구현하기 위해이 기술 만 사용합니다. "작은 문자열 최적화"는 문자열 객체 자체 내부의 저장소를 재사용하는 훌륭한 최적화 대안입니다. 큰 문자열의 경우 문자열 객체 내부의 공간은 동적으로의 일반적인 포인터를 저장합니다 할당 된 버퍼 및 버퍼의 크기와 같은 하우스 키핑 정보; 작은 문자열의 경우 문자열 공간을 직접 저장하고 동적 메모리 할당을 완전히 피하기 위해 동일한 공간이 대신 재사용됩니다. 작은 문자열 최적화 (및 다른 깊이의 다른 문자열 최적화 및 비관 화)에 대한 자세한 내용은 ...을 참조하십시오. "

덜 유용한 예제를 보려면 길지만 결정적이지 않은 질문 gcc, 엄격 앨리어싱 및 노조를 통한 캐스팅을 참조하십시오 .


23

글쎄, 내가 생각할 수있는 사용 사례의 예는 다음과 같습니다.

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} some32bittype;

그런 다음 해당 32 비트 데이터 블록의 8 비트 별도 부분에 액세스 할 수 있습니다. 그러나 엔디안에 의해 물릴 가능성에 대비하십시오.

이것은 하나의 가상의 예일 뿐이지 만 필드의 데이터를 이와 같은 구성 요소 부분으로 분할 할 때마다 공용체를 사용할 수 있습니다.

즉, 엔디안 안전 방법이 있습니다.

uint32_t x;
uint8_t a = (x & 0xFF000000) >> 24;

예를 들어, 이진 연산은 컴파일러에 의해 올바른 엔디안으로 변환됩니다.


나는 언제 노조를 사용해야하는지에 대한 질문을하는 것이 가장 좋다고 생각한다. 당신은 노동 조합이 올바른 도구 가 아닌 곳에 대한 답변을 제공했습니다 .이 답변에서 더 명확하게 생각해야합니다.
Michael

15

노동 조합에 대한 일부 용도 :

  • 알 수없는 외부 호스트에 일반 엔디안 인터페이스를 제공하십시오.
  • 네트워크 링크에서 VAX G_FLOATS 를 수락 하고 처리 를 위해 IEEE 754 long reals 로 변환하는 것과 같은 외부 CPU 아키텍처 부동 소수점 데이터를 조작 합니다 .
  • 더 높은 수준의 유형에 대한 간단한 비트 트위들 링 액세스를 제공합니다.
union {
      unsigned char   byte_v[16];
      long double     ld_v;
 }

이 선언을 사용하면 a의 16 진수 바이트 값을 표시하거나 long double지수 기호를 변경하거나 비정규 값인지 확인하거나 지원하지 않는 CPU에 대해 긴 이중 산술을 구현하는 등의 작업이 간단합니다 .

  • 필드가 특정 값에 종속되는 경우 저장 공간 절약 :

    class person {  
        string name;  
    
        char gender;   // M = male, F = female, O = other  
        union {  
            date  vasectomized;  // for males  
            int   pregnancies;   // for females  
        } gender_specific_data;
    }
  • 컴파일러에서 사용할 포함 파일을 정리하십시오. 수십에서 수백 가지 용도로 사용됩니다 union.

    [wally@zenetfedora ~]$ cd /usr/include
    [wally@zenetfedora include]$ grep -w union *
    a.out.h:  union
    argp.h:   parsing options, getopt is called with the union of all the argp
    bfd.h:  union
    bfd.h:  union
    bfd.h:union internal_auxent;
    bfd.h:  (bfd *, struct bfd_symbol *, int, union internal_auxent *);
    bfd.h:  union {
    bfd.h:  /* The value of the symbol.  This really should be a union of a
    bfd.h:  union
    bfd.h:  union
    bfdlink.h:  /* A union of information depending upon the type.  */
    bfdlink.h:  union
    bfdlink.h:       this field.  This field is present in all of the union element
    bfdlink.h:       the union; this structure is a major space user in the
    bfdlink.h:  union
    bfdlink.h:  union
    curses.h:    union
    db_cxx.h:// 4201: nameless struct/union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:typedef union
    _G_config.h:typedef union
    gcrypt.h:  union
    gcrypt.h:    union
    gcrypt.h:    union
    gmp-i386.h:  union {
    ieee754.h:union ieee754_float
    ieee754.h:union ieee754_double
    ieee754.h:union ieee854_long_double
    ifaddrs.h:  union
    jpeglib.h:  union {
    ldap.h: union mod_vals_u {
    ncurses.h:    union
    newt.h:    union {
    obstack.h:  union
    pi-file.h:  union {
    resolv.h:   union {
    signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val)
    stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait'
    stdlib.h:  (__extension__ (((union { __typeof(status) __in; int __i; }) \
    stdlib.h:/* This is the type of the argument to `wait'.  The funky union
    stdlib.h:   causes redeclarations with either `int *' or `union wait *' to be
    stdlib.h:typedef union
    stdlib.h:    union wait *__uptr;
    stdlib.h:  } __WAIT_STATUS __attribute__ ((__transparent_union__));
    thread_db.h:  union
    thread_db.h:  union
    tiffio.h:   union {
    wchar.h:  union
    xf86drm.h:typedef union _drmVBlank {

5
Tsk tsk! 두 개의 공감 비와 설명이 없습니다. 실망 스럽습니다.
wallyk

남자와 여자를 붙잡을 수있는 사람의 예는 내 눈에 매우 나쁜 디자인입니다. 개인베이스 클래스와 남자와 여자가 ​​왜 클래스를 파생시키지 않았습니까? 죄송하지만 데이터 필드에 저장된 유형을 결정하는 변수를 수동으로 찾는 것은 전혀 좋지 않습니다. 이것은 수년 동안 보지 못한 수제 C 코드입니다. 그러나 공감대는 없습니다, 그것은 단지 내 견해입니다 :-)
Klaus

4
나는 당신이 "거세 된"또는 "임신"노조에 대한 다운 보트를 얻었다 고 생각합니다. 조금 아파요.
akaltar

2
네, 어두운 날인 것 같아요
wallyk

14

유니언은 바이트 수준 (낮은 수준) 데이터를 처리 할 때 유용합니다.

최근 사용 된 것 중 하나는 다음과 같은 IP 주소 모델링이었습니다.

// Composite structure for IP address storage
union
{
    // IPv4 @ 32-bit identifier
    // Padded 12-bytes for IPv6 compatibility
    union
    {
        struct
        {
            unsigned char _reserved[12];
            unsigned char _IpBytes[4];
        } _Raw;

        struct
        {
            unsigned char _reserved[12];
            unsigned char _o1;
            unsigned char _o2;
            unsigned char _o3;
            unsigned char _o4;    
        } _Octet;    
    } _IPv4;

    // IPv6 @ 128-bit identifier
    // Next generation internet addressing
    union
    {
        struct
        {
            unsigned char _IpBytes[16];
        } _Raw;

        struct
        {
            unsigned short _w1;
            unsigned short _w2;
            unsigned short _w3;
            unsigned short _w4;
            unsigned short _w5;
            unsigned short _w6;
            unsigned short _w7;
            unsigned short _w8;   
        } _Word;
    } _IPv6;
} _IP;

7
그러나 이와 같은 원시 항목에 액세스하는 것은 표준이 아니며 모든 컴파일러에서 예상대로 작동하지 않을 수 있습니다.
nos

3
또한 정렬을 보장하지 않는 방식으로 이것이 사용되는 것을 보는 것이 매우 일반적이며, 이는 정의되지 않은 동작입니다.
Mooing Duck

10

유니온을 사용한 경우의 예 :

class Vector
{
        union 
        {
            double _coord[3];
            struct 
            {
                double _x;
                double _y; 
                double _z;
            };

        };
...
}

이를 통해 데이터를 배열 또는 요소로 액세스 할 수 있습니다.

다른 용어가 동일한 값을 가리 키도록 유니온을 사용했습니다. 이미지 처리에서 열이나 너비 또는 X 방향의 크기에 관계없이 작업하는 것은 혼란 스러울 수 있습니다. 이 문제를 해결하기 위해 조합을 사용하므로 어떤 설명이 함께 사용되는지 알 수 있습니다.

   union {   // dimension from left to right   // union for the left to right dimension
        uint32_t            m_width;
        uint32_t            m_sizeX;
        uint32_t            m_columns;
    };

    union {   // dimension from top to bottom   // union for the top to bottom dimension
        uint32_t            m_height;
        uint32_t            m_sizeY;
        uint32_t            m_rows;
    };

12
이 솔루션은 대부분의 관측 가능한 플랫폼에서 작동하지만 값을 _x, _y, _z로 설정하고 _coord에 액세스하는 것은 정의되지 않은 동작입니다. 노조의 주요 목적은 공간 보존입니다. 이전에 설정 한 것과 동일한 공용체 요소에 액세스해야합니다.
anxieux

1
이 내가 좌표 FORR 성병 :: 배열을 사용 althrough, 너무 그것을 사용하고, 일부 static_asserts 방법이다
빅토르 SEHR

1
이 코드는 엄격한 앨리어싱 규칙을 위반하므로 권장하지 않아야합니다.
Walter

이 일을 신뢰할 수 있도록 노동 조합을 개선 할 수있는 방법이 있습니까?
앤드류

8

C는 다형성을 제공합니다.


18
나는 그렇게 생각 void*했다 ^^

2
@ user166390 다형성은 동일한 인터페이스를 사용하여 여러 유형을 조작합니다. void *에는 인터페이스가 없습니다.
Alice

2
C에서, 다형성은 일반적으로 불투명 한 유형 및 / 또는 함수 포인터를 통해 구현됩니다. 나는 당신이 그것을 달성하기 위해 어떻게 또는 왜 노조를 사용할 것인지 전혀 모른다. 정말 나쁜 생각처럼 들립니다.
룬딘

7

통합의 훌륭한 사용법은 PCL (Point Cloud Library) 소스 코드에서 찾은 메모리 정렬입니다. API의 단일 데이터 구조는 SSE를 지원하는 CPU와 SSE를 지원하지 않는 CPU의 두 가지 아키텍처를 대상으로 할 수 있습니다. 예를 들어 : PointXYZ의 데이터 구조는

typedef union
{
  float data[4];
  struct
  {
    float x;
    float y;
    float z;
  };
} PointXYZ;

3 개의 플로트에는 SSE 정렬을위한 추가 플로트가 채워집니다. 그래서

PointXYZ point;

사용자는 x 좌표에 액세스하기 위해 point.data [0] 또는 point.x (SSE 지원에 따라)에 액세스 할 수 있습니다. 더 비슷한 더 나은 사용 자세한 내용은 다음 링크에 있습니다 : PCL 문서 PointT 유형


7

union여전히 ++ C에서 03 사용하면서 키워드, 1 , 주로 C 일의 잔재입니다. 가장 눈에 띄는 문제는 POD 1 에서만 작동한다는 것 입니다.

그러나 노조에 대한 아이디어는 여전히 존재하며 실제로 Boost 라이브러리에는 노조와 같은 클래스가 있습니다.

boost::variant<std::string, Foo, Bar>

union(모두가 아닐지라도) 장점의 대부분을 가지고 있으며 다음을 추가합니다.

  • 비 포드 유형을 올바르게 사용하는 기능
  • 정적 안전

실제로, 그것은 union+ 의 조합과 동등한 것으로 입증되었으며, 그것이 enum빠른 것으로 벤치마킹했습니다 ( RTTI를 사용하기 때문에 boost::any더 많은 영역입니다 dynamic_cast).

1 Union은 C ++ 11 ( 제한되지 않은 union ) 에서 업그레이드되었으며 , 사용자는 현재 활성 Union 멤버에서 소멸자를 수동으로 호출해야하지만 소멸자가있는 객체를 포함 할 수 있습니다. 변형을 사용하는 것이 훨씬 쉽습니다.


더 최신 버전의 c ++에서는 더 이상 적용되지 않습니다. 예를 들어 jrsala의 답변을 참조하십시오.
앤드류

@Andrew : 제한없는 공용체가있는 C ++ 11에서 소멸자가있는 유형을 공용체에 저장할 수 있다고 언급하여 답변을 업데이트했습니다. 나는 아직도 당신이 정말로 더 나은 사용하여 꺼져있는 내 입장 대기 태그 노동 조합boost::variant자신에 노동 조합을 사용하려고하는 것보다. 노조를 둘러싼 정의되지 않은 행동이 너무 많아서 그것을 올바르게 얻을 수있는 기회가 무섭습니다.
Matthieu M.

3

노조에 관한 Wikipedia 기사에서 :

공용체의 주요 유용성은 공간절약하는 것입니다. 공간절약 할 수 있기 때문에 여러 유형을 같은 공간에 저장할 수 있습니다. 연합은 또한 조 다형성을 제공합니다 . 그러나 유형을 확인하지 않으므로 다른 필드에서 올바른 필드에 액세스해야하는 것은 프로그래머의 책임입니다. 공용체 변수의 관련 필드는 일반적으로 다른 변수의 상태에 의해, 가능하면 둘러싸는 구조체에서 결정됩니다.

하나의 공통 C 프로그래밍 관용구는 공용체를 사용하여 값의 원시 표현에 의존하는 코드에서와 같이 공용체의 한 필드에 할당하고 다른 필드에서 읽음으로써 reinterpret_cast를 호출하는 것을 수행합니다.


2

C의 초창기 (예 : 1974 년에 문서화 됨)에 모든 구조는 해당 멤버의 공통 네임 스페이스를 공유했습니다. 각 멤버 이름은 유형 및 오프셋과 연관되었습니다. "wd_woozle"이 오프셋 12에서 "int" p인 경우 모든 구조 유형 의 포인터 가 주어지면 다음 p->wd_woozle과 같습니다 *(int*)(((char*)p)+12). 언어는 모든 구조 유형의 모든 구성원이 고유 한 이름을 가지고 요구 를 제외하고 는 명시 적으로 사용 된 모든 구조체가 공통의 초기 시퀀스로 취급 경우 멤버 이름의 재사용을 허용하는.

구조 유형을 무차별 적으로 사용할 수 있다는 사실은 구조가 겹치는 필드를 포함하는 것처럼 동작하도록하는 것을 가능하게했습니다. 예를 들어, 주어진 정의 :

struct float1 { float f0;};
struct byte4  { char b0,b1,b2,b3; }; /* Unsigned didn't exist yet */

코드는 "float1"유형의 구조를 선언 한 다음 "members"b0 ... b3을 사용하여 개별 바이트에 액세스 할 수 있습니다. 각 구조가 멤버에 대해 별도의 네임 스페이스를 받도록 언어가 변경되면 여러 가지 방식으로 액세스 할 수있는 기능에 의존하는 코드가 작동하지 않습니다. 서로 다른 구조 유형에 대해 네임 스페이스를 분리하는 값은 그러한 코드를 수용하기 위해 변경되도록 요구하기에 충분했지만 그러한 기술의 가치는 언어를 계속 지원하기 위해 언어를 확장하는 것을 정당화하기에 충분했습니다.

내에서 스토리지를 액세스 할 수있는 기능을 이용하기 위해 작성했던 코드 struct float1그것이 것처럼 struct byte4: 선언을 추가하여 새로운 언어로 작동하도록 만들 수 있습니다 union f1b4 { struct float1 ff; struct byte4 bb; };, 선언 객체 유형으로 union f1b4;보다는 struct float1, 그리고에 액세스 교체 f0, b0, b1, 등 .와 ff.f0, bb.b0, bb.b1같은 코드가 지원 될 수 있었다 더 나은 방법이 있지만, 등의 union접근 방식은 적어도 앨리어싱 규칙 C89 시대의 해석과, 적어도 어느 정도 가능한이었다.


1

n 가지 유형의 구성이 있다고 가정 해 봅시다 (매개 변수를 정의하는 변수 세트 일뿐입니다). 구성 유형의 열거를 사용하여 구성 유형의 ID를 가진 다른 모든 유형의 구성을 통합 한 구조를 정의 할 수 있습니다.

이렇게하면 구성을 전달할 때마다 ID를 사용하여 구성 데이터를 해석하는 방법을 결정할 수 있지만 구성이 큰 경우 각 잠재적 유형 낭비 공간에 대해 병렬 구조를 갖지 않아도됩니다.


1

최근 에 C 표준의 최신 버전에 도입 된 엄격한 앨리어싱 규칙 에 따라 노조 의 중요성이 높아지고 최근에 한 가지 향상 이 이루어졌습니다 .

C 표준을 위반하지 않고 공용체 do를 사용하여 유형 정리 를 할 수 있습니다 .
이 프로그램은이 지정되지 않은 행동을 (내가 가정 때문에 floatunsigned int같은 길이가) 아니라 정의되지 않은 동작을 합니다 ( 여기 ).

#include <stdio.h> 

union float_uint
{
    float f;
    unsigned int ui;
};

int main()
{
    float v = 241;
    union float_uint fui = {.f = v};

    //May trigger UNSPECIFIED BEHAVIOR but not UNDEFINED BEHAVIOR 
    printf("Your IEEE 754 float sir: %08x\n", fui.ui);

    //This is UNDEFINED BEHAVIOR as it violates the Strict Aliasing Rule
    unsigned int* pp = (unsigned int*) &v;

    printf("Your IEEE 754 float, again, sir: %08x\n", *pp);

    return 0;
}

유형 접근 규칙은 단지 "최근"버전의 표준이 아닙니다. C의 모든 버전에는 기본적으로 동일한 규칙이 포함되어 있습니다. 변경된 사항은 각주를 고려하기 위해 사용 된 컴파일러입니다. "이 목록의 목적은 개체의 별칭이 지정되거나 별칭이 지정되지 않을 수있는 환경을 지정하는 것입니다." 작성된대로 앨리어싱 포함하지 않는 경우에는 규칙이 적용되지 않았 음을 나타내지 만 이제는 존재하지 않는 앨리어싱을 만들기 위해 코드를 다시 작성하라는 초대로 간주합니다.
supercat

1

내가 조합을 사용하기위한 하나의 좋은 실용적인 예제를 추가하고 싶습니다 - 공식 계산기 / 인터프리터를 구현하거나 계산에서의 어떤 종류를 사용하여 (예를 들어, 당신은 사용할 런타임 동안 modificable 당신의 계산 공식의 일부 - 수치 방정식을 해결 - 단지 예를 들어). 따라서 다음과 같이 다른 유형 (정수, 부동 소수점, 복잡한 숫자)의 숫자 / 상수를 정의 할 수 있습니다.

struct Number{
enum NumType{int32, float, double, complex}; NumType num_t;
union{int ival; float fval; double dval; ComplexNumber cmplx_val}
}

따라서 메모리를 절약하고 더 중요한 것은-클래스 상속 / 다형성을 통한 구현과 비교하여 극소량 (런타임 정의 수를 많이 사용하는 경우)의 작은 객체에 대한 동적 할당을 피하십시오. 그러나 더 흥미로운 점은이 유형의 구조체와 함께 C ++ 다형성의 힘을 사용할 수 있습니다 (예 : 이중 디스패치의 팬이라면). 모든 숫자 유형의 부모 클래스에 "더미"인터페이스 포인터를이 구조체의 필드 로 추가하고 원시 유형 대신 / 이 인스턴스 를 가리 키 거나 기존 C 함수 포인터를 사용하십시오.

struct NumberBase
{
virtual Add(NumberBase n);
...
}
struct NumberInt: Number
{
//implement methods assuming Number's union contains int
NumberBase Add(NumberBase n);
...
}
struct NumberDouble: Number
{
 //implement methods assuming Number's union contains double
 NumberBase Add(NumberBase n);
 ...
}
//e.t.c. for all number types/or use templates
struct Number: NumberBase{
 union{int ival; float fval; double dval; ComplexNumber cmplx_val;}
 NumberBase* num_t;
 Set(int a)
 {
 ival=a;
  //still kind of hack, hope it works because derived classes of   Number    dont add any fields
 num_t = static_cast<NumberInt>(this);
 }
}

따라서 스위치 (유형)-메모리 효율적인 구현 (작은 객체의 동적 할당 없음)-유형 검사 대신 다형성을 사용할 수 있습니다 (필요한 경우).


동적 언어를 만들 때 유용 할 수 있습니다. 내가 해결할 것이라고 생각하는 문제는 N 번 수정하지 않고 알 수없는 유형의 변수를 질량으로 수정하는 것입니다. 매크로는 이것에 대해 끔찍하며 템플릿은 사실상 불가능합니다.
Andrew

0

에서 http://cplus.about.com/od/learningc/ss/lowlevel_9.htm :

노조의 사용은 거의 없으며 그 사이에 있습니다. 대부분의 컴퓨터에서 포인터와 int의 크기는 일반적으로 동일합니다. 이는 일반적으로 CPU의 레지스터에 맞기 때문입니다. 따라서 int 또는 다른 방법으로 포인터를 빠르고 더러운 캐스트하려면 Union을 선언하십시오.

union intptr {   int i;   int * p; }; 
union intptr x; x.i = 1000; 
/* puts 90 at location 1000 */ 
*(x.p)=90; 

다른 크기의 메시지를 보내고받는 명령 또는 메시지 프로토콜에서 공용체를 사용할 수도 있습니다. 각 메시지 유형은 서로 다른 정보를 보유하지만 각각 고정 된 부분 (아마도 구조체)과 가변 부분 비트를 갖습니다. 이것이 당신이 그것을 구현하는 방법입니다 ..

struct head {   int id;   int response;   int size; }; struct msgstring50 {    struct head fixed;    char message[50]; } struct

struct msgstring80 {구조체 헤드 고정; 문자 메시지 [80]; }
struct msgint10 {구조체 헤드 고정; int 메시지 [10]; } 구조체 msgack {구조체 헤드 고정; int ok; } 통합 메시지 유형 {
struct msgstring50 m50; 구조체 msgstring80 m80; 구조체 msgint10 i10; 구조체 msgack ack; }

실제로 공용체의 크기는 동일하지만 공간을 낭비하지 않고 의미있는 데이터 만 보내는 것이 좋습니다. msgack의 크기는 16 바이트이고 msgstring80의 크기는 92 바이트입니다. 따라서 메시지 유형 변수가 초기화되면 유형에 따라 크기 필드가 설정됩니다. 그런 다음 다른 함수에서 올바른 바이트 수를 전송하는 데 사용할 수 있습니다.


0

유니온은 프로그램에 기계 독립적 정보를 포함시키지 않고 단일 스토리지 영역에서 다른 종류의 데이터를 조작하는 방법을 제공합니다. 파스칼의 변형 레코드와 유사합니다.

컴파일러 심볼 테이블 관리자에서 볼 수있는 것과 같은 예로 상수는 정수, 부동 소수점 또는 문자 포인터 일 수 있습니다. 특정 상수의 값은 적절한 유형의 변수에 저장해야하지만 값이 동일한 양의 스토리지를 차지하고 유형에 관계없이 동일한 위치에 저장되는 경우 테이블 관리에 가장 편리합니다. 이는 여러 유형 중 하나를 합법적으로 보유 할 수있는 단일 변수 인 공용체의 목적입니다. 구문은 구조를 기반으로합니다.

union u_tag {
     int ival;
     float fval;
     char  *sval;
} u;

변수 u는 세 가지 유형 중 가장 큰 것을 보유 할 수있을 정도로 커질 것입니다. 특정 크기는 구현에 따라 다릅니다. 사용법이 일관된 한 이러한 유형을 u에 할당 한 다음 표현식에 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.