programing

왜 C연합이 필요한가?

prostudy 2022. 5. 10. 22:23
반응형

왜 C연합이 필요한가?

언제 조합을 사용해야 하는가?왜 우리는 그것들이 필요한가?

조합은 종종 정수와 부유물의 이항 표현 사이에서 전환하는데 사용된다.

union
{
  int i;
  float f;
} u;

// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);

비록 이것은 기술적으로 C 표준에 따라 정의되지 않은 행동이지만(가장 최근에 작성된 필드만 읽도록 되어 있다), 사실상 어떤 컴파일러에서도 잘 정의된 방식으로 행동할 것이다.

또한 조합은 C에서 의사-폴리모르프리즘을 구현하기 위해 사용되기도 하는데, 구조물에 어떤 종류의 사물을 포함하고 있는지를 나타내는 태그를 부여한 다음 가능한 유형을 함께 결합하는 것이다.

enum Type { INTS, FLOATS, DOUBLE };
struct S
{
  Type s_type;
  union
  {
    int s_ints[2];
    float s_floats[2];
    double s_double;
  };
};

void do_something(struct S *s)
{
  switch(s->s_type)
  {
    case INTS:  // do something with s->s_ints
      break;

    case FLOATS:  // do something with s->s_floats
      break;

    case DOUBLE:  // do something with s->s_double
      break;
  }
}

이렇게 하면 다음 크기의struct S28바이트가 아니라 12바이트에 불과하다.

조합은 임베디드 프로그래밍이나 하드웨어/메모리에 직접 액세스해야 하는 상황에서 특히 유용하다.여기 사소한 예가 있다.

typedef union
{
    struct {
        unsigned char byte1;
        unsigned char byte2;
        unsigned char byte3;
        unsigned char byte4;
    } bytes;
    unsigned int dword;
} HW_Register;
HW_Register reg;

그러면 다음과 같이 레지스트리에 액세스할 수 있다.

reg.dword = 0x12345678;
reg.bytes.byte3 = 4;

Endianness(바이트 순서)와 프로세서 아키텍처는 물론 중요하다.

또 다른 유용한 기능은 비트 수식어:

typedef union
{
    struct {
        unsigned char b1:1;
        unsigned char b2:1;
        unsigned char b3:1;
        unsigned char b4:1;
        unsigned char reserved:4;
    } bits;
    unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;

이 코드를 사용하면 레지스터/메모리 주소에서 단일 비트에 직접 액세스할 수 있다.

x = reg.bits.b2;

낮은 수준의 시스템 프로그래밍이 적절한 예다.

IERC, 하드웨어 레지스터를 컴포넌트 비트로 분해하기 위해 유니언을 사용해 왔다.그래서 당신은 8비트 레지스터에 접속할 수 있다. (이것은 내가 이것을 했던 그 날의 ;-)

(정확한 구문은 잊어버렸지만...)이 구조는 제어 레지스터를 control_byte 또는 개별 비트를 통해 접근할 수 있게 한다.비트가 특정 내성에 대해 올바른 레지스터 비트에 매핑되도록 하는 것이 중요할 것이다.

typedef union {
    unsigned char control_byte;
    struct {
        unsigned int nibble  : 4;
        unsigned int nmi     : 1;
        unsigned int enabled : 1;
        unsigned int fired   : 1;
        unsigned int control : 1;
    };
} ControlRegister;

사용량이 많다.그냥 해라grep union /usr/include/*또는 유사한 디렉토리에.대부분의 경우:union에 싸여 있다struct그리고 구조체의 한 구성원은 유니온에서 어떤 요소에 접근해야 하는지 알려준다.예를 들어 체크아웃man elf실제 구현을 위해

기본 원칙은 다음과 같다.

struct _mydata {
    int which_one;
    union _data {
            int a;
            float b;
            char c;
    } foo;
} bar;

switch (bar.which_one)
{
   case INTEGER  :  /* access bar.foo.a;*/ break;
   case FLOATING :  /* access bar.foo.b;*/ break;
   case CHARACTER:  /* access bar.foo.c;*/ break;
}

나는 그것을 두어 개의 도서관에서 객체 지향 상속을 대체하는 것으로 보아 왔다.

예시

        Connection
     /       |       \
  Network   USB     VirtualConnection

연결 "클래스"를 위의 항목 중 하나로 지정하려면 다음과 같은 내용을 입력하십시오.

struct Connection
{
    int type;
    union
    {
        struct Network network;
        struct USB usb;
        struct Virtual virtual;
    }
};

libinfinity에서 사용 예: http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63b754c6b5ec234cbf4dfc;hb=HEAD#l74

조합은 상호 배타적인 데이터 구성원이 동일한 메모리를 공유할 수 있도록 허용한다.이것은 임베디드 시스템과 같이 메모리가 더 부족할 때 상당히 중요하다.

다음 예시:

union {
   int a;
   int b;
   int c;
} myUnion;

이 결합은 3개의 별도 int 값이 아닌 하나의 int의 공간을 차지할 것이다.사용자가 a 을 설정한 다음 b 값을 설정하면 둘 다 동일한 메모리 위치를 공유하기 때문에 a 을 덮어쓰게 된다.

여기 내 코드베이스(기억과 파라프레이를 통해 정확하지 않을 수 있음)에서 나온 조합의 예가 있다.그것은 내가 만든 통역관에 언어 요소를 저장하는 데 사용되었다.예를 들어 다음 코드를 입력하십시오.

set a to b times 7.

다음과 같은 언어 요소로 구성된다.

  • 기호[세트]
  • 가변[a]
  • 상징[토]
  • 가변[b]
  • 상징[기호]
  • 상수[7]
  • 기호[[]

언어 요소는 '로 정의되었다.#define 값:

#define ELEM_SYM_SET        0
#define ELEM_SYM_TO         1
#define ELEM_SYM_TIMES      2
#define ELEM_SYM_FULLSTOP   3
#define ELEM_VARIABLE     100
#define ELEM_CONSTANT     101

그리고 각 원소를 저장하기 위해 다음과 같은 구조를 사용하였다.

typedef struct {
    int typ;
    union {
        char *str;
        int   val;
    }
} tElem;

그 다음 각 요소의 크기는 최대 조합의 크기였다(타입의 경우 4바이트, 조합의 경우 4바이트, 일반적인 값이지만 실제 크기는 구현에 따라 달라짐).

"set" 요소를 생성하려면 다음을 사용하십시오.

tElem e;
e.typ = ELEM_SYM_SET;

"변수[b]" 요소를 생성하려면 다음을 사용하십시오.

tElem e;
e.typ = ELEM_VARIABLE;
e.str = strdup ("b");   // make sure you free this later

상수[7] 요소를 생성하려면 다음을 사용하십시오.

tElem e;
e.typ = ELEM_CONSTANT;
e.val = 7;

그리고 부유물을 포함하도록 쉽게 확장할 수 있다.float flt은 이성()이다.struct ratnl {int num; int denom;} 및 기타 유형.

기본 전제는 다음과 같다.str그리고val메모리에 연속되지 않고 실제로 중복되므로, 구조가 메모리 위치에 기반을 둔 여기에 설명된 것과 같은 메모리 블록에 대해 다른 보기를 얻을 수 있는 방법이다.0x1010정수와 포인터는 모두 4바이트 입니다.

       +-----------+
0x1010 |           |
0x1011 |    typ    |
0x1012 |           |
0x1013 |           |
       +-----+-----+
0x1014 |     |     |
0x1015 | str | val |
0x1016 |     |     |
0x1017 |     |     |
       +-----+-----+

만약 그것이 단지 하나의 구조 안에 있다면, 다음과 같이 보일 것이다.

       +-------+
0x1010 |       |
0x1011 |  typ  |
0x1012 |       |
0x1013 |       |
       +-------+
0x1014 |       |
0x1015 |  str  |
0x1016 |       |
0x1017 |       |
       +-------+
0x1018 |       |
0x1019 |  val  |
0x101A |       |
0x101B |       |
       +-------+

다른 방법으로 사용될 수 있는 메모리, 즉 메모리 절약이 더 쉬워진다고 할 수 있다.예를 들어, 숫자뿐만 아니라 짧은 문자열도 저장할 수 있는 "변수" 구조를 수행하고자 할 경우:

struct variant {
    int type;
    double number;
    char *string;
};

32비트 시스템에서 이는 각 인스턴스에 대해 최소 96비트 또는 12바이트를 사용하게 될 것이다.variant.

유니언을 사용하여 크기를 64비트 또는 8바이트로 줄일 수 있다.

struct variant {
    int type;
    union {
        double number;
        char *string;
    } value;
};

다양한 변수 유형 등을 추가하고자 할 경우 훨씬 더 많은 비용을 절약할 수 있다.보이드 포인터를 던지는 비슷한 일을 할 수 있다는 것이 사실일 수도 있지만, 조합은 활자 안전뿐만 아니라 훨씬 더 쉽게 접근할 수 있게 해준다.이러한 절감 효과는 크지 않지만, 이 구조의 모든 경우에 사용되는 메모리의 3분의 1을 절약할 수 있다.

유니언은 메모리를 절약하기 위해 사용되며, 특히 메모리가 중요한 메모리가 제한된 장치에서 사용된다.Exp:

union _Union{
  int a;
  double b;
  char c;
};

예를 들어, 메모리가 제한된 시스템에서 위의 3가지 데이터 유형(inte, double, char)이 필요하다고 하자.만약 우리가 "조합"을 사용하지 않는다면, 우리는 이 3가지 데이터 유형을 정의해야 한다.이 경우 (a) + (b) + (c) 메모리 공간이 할당된다.그러나 만약 우리가 양파를 사용한다면, 오직 하나의 메모리 공간만이 이 3가지 데이터 유형에서 가장 큰 데이터 t ype에 따라 할당될 것이다.왜냐하면 조합 구조의 모든 변수는 동일한 메모리 공간을 사용할 것이기 때문이다.따라서 가장 큰 데이터 유형에 따라 할당된 메모리 공간은 모든 변수의 공통 공간이 될 것이다.예를 들면 다음과 같다.

union _Union{
int a;
double b;
char c;
};

int main() {
 union _Union uni;
 uni.a = 44;
 uni.b = 144.5;
 printf("a:%d\n",uni.a);
 printf("b:%lf\n",uni.b);
 return 0;
 }

출력: a: 0 및 b:144.500000

왜 a는 0인가?왜냐하면 조합구조는 하나의 메모리 영역만 가지고 있고 모든 데이터 구조는 그것을 공통적으로 사용하기 때문이다.그래서 마지막 할당된 값이 이전 값을 덮어쓴다.한 가지 더 예:

 union _Union{
    char name[15];
    int id;
};


int main(){
   union _Union uni;
   char choice;
   printf("YOu can enter name or id value.");
   printf("Do you want to enter the name(y or n):");
   scanf("%c",&choice);
   if(choice == 'Y' || choice == 'y'){
     printf("Enter name:");
     scanf("%s",uni.name);
     printf("\nName:%s",uni.name);
   }else{
     printf("Enter Id:");
     scanf("%d",&uni.id);
     printf("\nId:%d",uni.id);
   }
return 0;
}

참고:조합의 크기는 래리 크기 필드를 저장하기 위해 충분한 수의 바이트를 예약해야 하기 때문에 가장 큰 필드 크기입니다.

이 답들 중 다수는 한 유형에서 다른 유형으로 캐스팅을 다룬다.동일한 유형의 유니언(즉, 직렬 데이터 스트림을 구문 분석할 때)에서 가장 많이 사용한다.그들은 프레임 패킷의 파싱/구성을 사소한 것으로 만들도록 한다.

typedef union
{
    UINT8 buffer[PACKET_SIZE]; // Where the packet size is large enough for
                               // the entire set of fields (including the payload)

    struct
    {
        UINT8 size;
        UINT8 cmd;
        UINT8 payload[PAYLOAD_SIZE];
        UINT8 crc;
    } fields;

}PACKET_T;

// This should be called every time a new byte of data is ready 
// and point to the packet's buffer:
// packet_builder(packet.buffer, new_data);

void packet_builder(UINT8* buffer, UINT8 data)
{
    static UINT8 received_bytes = 0;

    // All range checking etc removed for brevity

    buffer[received_bytes] = data;
    received_bytes++;

    // Using the struc only way adds lots of logic that relates "byte 0" to size
    // "byte 1" to cmd, etc...
}

void packet_handler(PACKET_T* packet)
{
    // Process the fields in a readable manner
    if(packet->fields.size > TOO_BIG)
    {
        // handle error...
    }

    if(packet->fields.cmd == CMD_X)
    {
        // do stuff..
    }
}

편집 엔디안성과 구조체 패딩에 대한 코멘트는 유효하고 큰 우려를 나타낸다.나는 이 코드 본문을 거의 전적으로 임베디드 소프트웨어에 사용했는데, 대부분은 내가 파이프의 양쪽 끝을 통제하고 있었다.

학교에서 나는 다음과 같은 노조를 사용했다.

typedef union
{
  unsigned char color[4];
  int       new_color;
}       u_color;

<< 연산자>와 << 연산자>를 사용하는 대신, 색상을 좀더 쉽게 다루기 위해 그것을 이용했다.

C의 초기 버전에서 모든 구조 선언은 공통 필드 집합을 공유한다.주어진:

struct x {int x_mode; int q; float x_f};
struct y {int y_mode; int q; int y_l};
struct z {int z_mode; char name[20];};

컴파일러는 기본적으로 구조물의 크기 표(및 가능한 정렬)와 구조물의 구성원 이름, 유형 및 간격띄우기의 별도 표를 생성한다.컴파일러는 어떤 구성원이 어떤 구조물에 속하는지 추적하지 않았으며, 유형과 오프셋이 일치하는 경우에만 두 구조물이 동일한 이름의 구성원을 가질 수 있도록 허용했다(구성원과 동일).qstruct x그리고struct yp가 어떤 구조 유형에 대한 포인터라면 p->q는 "q"의 오프셋을 p에 추가하고 결과 주소에서 "int"를 가져올 것이다.

위의 의미론에 비추어 볼 때, 함수에 의해 사용되는 모든 필드가 해당 구조물 내에 유용한 필드와 일렬로 늘어선다면, 여러 종류의 구조물에 대해 어떤 유용한 연산을 상호 교환적으로 수행할 수 있는 함수 작성이 가능했다.이것은 유용한 특징이었고, C를 변경하여 해당 구조물의 유형에 대한 구조 접근에 사용되는 구성원의 유효성을 확인하면 동일한 주소에 여러 개의 명명된 필드를 포함할 수 있는 구조가 없는 경우 C를 잃게 되는 것을 의미했을 것이다.C에 "유니온" 유형을 추가하는 것은 그 공백을 어느 정도 메우는 데 도움이 되었다(그렇지는 않지만, IMHO도 그랬어야 하는 것뿐만 아니라).

그 공백을 메우는 노조의 능력의 본질적인 부분은 조합원에 대한 포인터가 그 조합원을 포함하는 어떤 조합에 대한 포인터로 변환될 수 있고, 어떤 조합에 대한 포인터가 어떤 조합원에 대한 포인터로 변환될 수 있다는 사실이었다.C89 기준서에서 명시적으로 '주조'를 규정하지는 않았지만T* 에.U*두 가지를 모두 포함하는 조합 유형에 대한 포인터로 주물렀던 것과 같음T그리고U, 그리고 나서 그것을 던진다.U*후자의 주물 시퀀스의 정의된 동작은 사용된 조합 유형에 영향을 받지 않을 것이며, 이 기준서는 다음의 직접 주물에 대해 어떠한 반대 의미도 명시하지 않았다.T로.U. 또한, 함수가 출처를 알 수 없는 포인터를 받은 경우, 다음을 통해 객체를 쓰는 동작T* , 변환T*완전히U*, 그리고 나서 그 물체를 읽는다.U*조합원을 통해 조합원을 작성하는 것과 같을 것이다.T그리고 유형으로 읽는 것.U몇 가지 경우(예: 공통 초기 시퀀스 멤버에 액세스할 때)에 표준으로 정의되고 나머지는 구현 정의되지 않음(미정의 경우 제외)으로 정의된다.프로그램이한 CIS보증을 조합 유형의 실제 개체로 악용하는 경우는 드물었지만, 출처를 알 수 없는 개체에 대한 포인터가 조합원들에게 포인터처럼 행동해야 하고, 이와 연관된 행동 보증을 가져야 한다는 사실을 악용하는 것은 훨씬 일반적이었다.프로그램이한 CIS보증을 조합 유형의 실제 개체로 악용하는 경우는 드물었지만, 출처를 알 수 없는 개체에 대한 포인터가 조합원들에게 포인터처럼 행동해야 하고, 이와 연관된 행동 보증을 가져야 한다는 사실을 악용하는 것은 훨씬 일반적이었다.

이런 유연한 구조가 필요한 특정한 경우를 생각하기는 어렵지만, 아마도 다양한 크기의 메시지를 보낼 수 있는 메시지 프로토콜에서, 하지만 그 때조차도 더 좋고 프로그래머 친화적인 대안이 있을 것이다.

조합은 다른 언어에서 변형된 형태와 약간 비슷하다. 조합은 한 번에 한 가지만 담을 수 있지만, 어떻게 선언하느냐에 따라 인트, 플로트 등이 될 수 있다.

예를 들면 다음과 같다.

typedef union MyUnion MYUNION;
union MyUnion
{
   int MyInt;
   float MyFloat;
};

MyUnion은 가장 최근에 설정한 OR 플로트만 포함할 것이다.이렇게 하면:

MYUNION u;
u.MyInt = 10;

u는 현재 10과 같은 인트를 가지고 있다.

u.MyFloat = 1.0;

u는 현재 1.0과 같은 플로트를 가지고 있다.그것은 더 이상 관여하지 않는다.이제 printf("MyInt=%d"), u.MyInt); 그러면 아마 당신은 실수를 하게 될 겁니다. 구체적인 행동에 대해서는 확신할 수 없지만.

조합의 크기는 가장 큰 분야인 이 경우 플로트의 크기에 의해 결정된다.

조합은 대단하다.내가 본 노동조합의 한 가지 교묘한 사용은 사건을 정의할 때 그것들을 사용하는 것이다.예를 들어, 이벤트를 32비트라고 결정할 수 있다.

이제 32비트 내에서 이벤트 발신자의 식별자로 처음 8비트를 지정하면...어떤 때는 사건 전체를 다루기도 하고, 어떤 때는 사건을 해부해서 그 구성 요소들을 비교하기도 한다.노동조합은 당신에게 두 가지 모두를 할 수 있는 유연성을 준다.

유니온 이벤트{서명되지 않은 긴 eventCode;서명되지 않은 char eventParts[4];
};

유니언은 하드웨어, 장치 또는 네트워크 프로토콜에 의해 정의된 구조물을 모델링하고자 할 때 또는 많은 수의 객체를 생성하고 공간을 절약하고자 할 때 사용된다.95%는 필요 없지만 쉽게 해독할 수 있는 코드를 고수하십시오.

함수의 기능에 따라 달라질 수 있는 값을 반환하는 기능이 있는 경우 결합을 사용하십시오.

COM 인터페이스에서 사용되는 것은?'유형'이라는 두 가지 분야와 '유형' 분야에 따라 처리되는 실제 가치를 가진 조합이 있다.

나는 임베디드 기기를 코딩할 때 유니언을 사용했다.나는 16비트 길이의 C를 가지고 있다.그리고 EEPROM에서 읽어야 할 때는 더 높은 8비트와 더 낮은 8비트를 회수해야 한다. 그래서 나는 다음과 같은 방법을 사용했다.

union data {
    int data;
    struct {
        unsigned char higher;
        unsigned char lower;
    } parts;
};

그것은 이동할 필요가 없으므로 코드를 읽기 쉽다.

반면에, 나는 stl 할당자를 위해 유니언을 사용하는 몇몇 오래된 C++ stl 코드를 보았다.관심이 있으면 sgi stl 소스 코드를 읽을 수 있다.여기 그 중 하나가 있다.

union _Obj {
    union _Obj* _M_free_list_link;
    char _M_client_data[1];    /* The client sees this.        */
};
  • 다른 레코드 유형을 포함하는 파일.
  • 다른 요청 유형을 포함하는 네트워크 인터페이스.

X.25 버퍼 명령 처리 기능

가능한 많은 X.25 명령 중 하나를 버퍼로 수신하고 가능한 모든 구조물의 유니언을 사용하여 제자리에 처리한다.

간단하고 매우 유용한 예는...이다.

상상:

당신은 A를 가지고 있다.uint32_t array[2]그리고 바이트 체인의 3번째와 4번째 바이트에 접근하기를 원한다.할 수 있다*((uint16_t*) &array[1])하지만 이것은 슬프게도 엄격한 가명 규칙을 어긴다!

그러나 알려진 컴파일러는 다음과 같은 일을 할 수 있게 해준다.

union un
{
    uint16_t array16[4];
    uint32_t array32[2];
}

엄밀히 말하면 이것은 여전히 규칙 위반이다.그러나 알려진 모든 표준은 이러한 사용을 지지한다.

참조URL: https://stackoverflow.com/questions/252552/why-do-we-need-c-unions

반응형