구조와 노조의 차이점


411

a struct와 a 의 차이를 나타내는 좋은 예가 union있습니까? 기본적으로 struct멤버의 모든 메모리를 union사용하고 가장 큰 멤버 메모리 공간 을 사용 한다는 것을 알고 있습니다. 다른 OS 수준의 차이가 있습니까?

답변:


677

공용체를 사용하면 요소가 모두 같은 위치에 저장되므로 요소 중 하나만 사용해야합니다. 여러 유형 중 하나 일 수있는 것을 저장하려는 경우 유용합니다. 반면에 구조체는 각 요소에 대해 별도의 메모리 위치를 가지며 모든 요소를 ​​한 번에 사용할 수 있습니다.

그들의 사용에 대한 구체적인 예를 들기 위해 나는 얼마 전에 Scheme 인터프리터에서 일하고 있었고 Scheme 데이터 유형을 본질적으로 C 데이터 유형에 오버레이했습니다. 여기에는 구조체에 값 유형을 나타내는 열거 형과 해당 값을 저장하는 공용체를 저장하는 작업이 포함되었습니다.

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

편집 : xb를 'c'로 설정하는 것이 궁금하다면 xa의 값을로 바꾸면 기술적으로 정의되지 않습니다. 대부분의 현대 컴퓨터에서 char은 1 바이트이고 int는 4 바이트이므로 xb에 값 'c'를 주면 xa의 첫 번째 바이트에도 동일한 값이 부여됩니다.

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

인쇄물

99, 99

두 값이 같은 이유는 무엇입니까? int 3의 마지막 3 바이트는 모두 0이므로 99로도 읽습니다. xa에 더 큰 숫자를 넣으면 항상 그렇지는 않습니다.

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

인쇄물

387427, 99

실제 메모리 값을 자세히 보려면 ​​16 진수로 값을 설정하고 인쇄 해 보겠습니다.

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

인쇄물

deadbe22, 22

0x22가 0xEF를 덮어 쓴 위치를 명확하게 볼 수 있습니다.

그러나

C에서는 int의 바이트 순서가 정의 되어 있지 않습니다. 이 프로그램은 내 Mac에서 0xEF를 0x22로 덮어 썼지 만 int를 구성하는 바이트 순서가 반대로되어 0xDE를 덮어 쓰는 다른 플랫폼이 있습니다. 따라서 프로그램을 작성할 때 이식 가능하지 않기 때문에 통합에서 특정 데이터를 덮어 쓰는 동작에 의존해서는 안됩니다.

바이트 순서에 대한 자세한 내용은 endianness를 확인하십시오 .


1
xb = 'c'인 경우 xa에 무엇이 저장됩니까? char의 참조 번호입니까?
kylex December

1
희망 당신은 XB을 설정할 때 XA에 저장되어 있는지 더 자세히 설명하는
카일 크로닌

1
@KyleCronin 나는 그것을 얻는 것 같아요. 귀하의 경우 유형 만 사용하면 하나만 사용해야한다는 것을 알고 있지만 런타임까지 어느 것을 알지 못하므로 노조가 그렇게 할 수 있습니다. 감사합니다
user12345613

2
@ user12345613 공용체는 구조체의 일종의 기본 클래스로 사용할 수 있습니다. 구조체 조합을 사용하여 OO 계층 구조를 에뮬레이션 할 수 있습니다.
Morten Jensen

1
멀티 바이트 유형의 @Lazar 바이트 순서는 엔디안에 따라 다릅니다. Wikipedia 기사를 읽는 것이 좋습니다.
Kyle Cronin

83

짧은 대답은 다음과 같습니다. 구조체는 레코드 구조입니다. 구조체의 각 요소는 새로운 공간을 할당합니다. 그래서 같은 구조체

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

(sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))각 인스턴스에 대해 최소 바이트를 메모리에 할당합니다. (아키텍처 정렬 제약 조건으로 인해 컴파일러가 구조체를 채울 수 있기 때문에 "적어도")

반면에

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

하나의 메모리 청크를 할당하고 네 개의 별명을 제공합니다. 따라서 sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double))다시 정렬에 대한 추가 가능성이 있습니다.


53

'struct'와 'union'의 차이점을 보여주는 좋은 예가 있습니까?

가상의 통신 프로토콜

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

이 가상의 프로토콜에서 "메시지 유형"에 따라 헤더의 다음 위치는 요청 번호이거나 4 자 코드이지만 둘다는 아닙니다. 요컨대, 공용체는 동일한 저장 영역 위치가 둘 이상의 데이터 유형을 나타내도록 허용합니다. 여기서 한 번에 하나의 데이터 유형 만 저장하려는 것이 보장됩니다.

유니온은 시스템 프로그래밍 언어로서 C의 유산에 기반을 둔 하위 수준의 세부 사항으로, "겹치는"저장 위치가 때때로 이런 식으로 사용됩니다. 한 번에 여러 유형 중 하나만 저장되는 데이터 구조가있는 경우 공용체를 사용하여 메모리를 절약 할 수 있습니다.

일반적으로 OS는 구조체와 공용체를 신경 쓰거나 알지 못합니다. 모두 단순히 메모리 블록입니다. 구조체는 여러 데이터 객체를 저장하는 메모리 블록으로, 객체가 겹치지 않습니다. 공용체는 여러 데이터 개체를 저장하지만 이들 중 가장 큰 데이터 만 저장할 수있는 메모리 블록이므로 한 번에 하나의 데이터 개체 만 저장할 수 있습니다.


1
네. 이것은 유스 케이스를 잘 설명합니다!
기드온

1
당신이 있다고 가정 packetheader ph;하는 방법을 당신에게 할 액세스 requestnumber를? ph.request.requestnumber?
justin.m.chase 오전

최고의 설명! 감사.
84RR1573R

39

귀하의 질문에 당신이 이미 상태로 사이의 주요 차이점 unionstructunion동안 회원, 노동 조합의 sizeof 연산자는 하나라는 것을 서로 정도의 메모리를 오버레이 struct회원 (사이에서 선택 패딩) 각각 하나씩 배치됩니다. 또한 공용체는 모든 멤버를 포함 할 수있을만큼 충분히 크며 모든 멤버에 맞는 정렬을 갖습니다. 따라서 int2 바이트 주소에만 저장할 수 있고 너비는 2 바이트이고 long은 4 바이트 주소에만 저장할 수 있으며 4 바이트 길이 라고 가정 해 봅시다 . 다음 노조

union test {
    int a;
    long b;
}; 

가질 수있다 sizeof(4), 그리고 4. 노동 조합 모두의 정렬 요구 사항 및 구조체는 시작 부분에서 끝 부분에 패딩을 가지고 있지만, 수 없습니다. 구조체에 쓰면 쓴 멤버의 값만 변경됩니다. 공용체 멤버에게 쓰면 다른 모든 멤버의 값이 유효하지 않게됩니다. 이전에 작성한 적이 없으면 액세스 할 수 없습니다. 그렇지 않으면 동작이 정의되지 않습니다. GCC는 가장 최근에 쓰지 않았더라도 실제로 노조원으로부터 읽을 수있는 확장 기능을 제공합니다. 운영 체제의 경우 사용자 프로그램이 공용체 또는 구조에 기록하는지 여부는 중요하지 않습니다. 이것은 실제로 컴파일러의 문제입니다.

공용체와 구조체의 또 다른 중요한 속성은 포인터가 멤버의 유형을 가리킬 수 있다는 입니다. 따라서 다음이 유효합니다.

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointer는 int*또는을 가리킬 수 있습니다 double*. 이 유형의 주소를 캐스팅하는 경우 testint*, 그것의 첫 번째 멤버, 가리 킵니다 a사실. 노동 조합도 마찬가지입니다. 따라서 공용체는 항상 올바른 정렬을 갖기 때문에 공용체를 사용하여 일부 유형을 유효하게 만들 수 있습니다.

union a {
    int a;
    double b;
};

그 조합은 실제로 int와 double을 가리킬 수 있습니다.

union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;    

C99 표준에 명시된 바와 같이 실제로 유효합니다.

객체는 다음 유형 중 하나를 갖는 lvalue 표현식으로 만 저장된 값에 액세스해야합니다.

  • 객체의 유효 유형과 호환되는 유형
  • ...
  • 구성원 중 위에서 언급 한 유형 중 하나를 포함하는 집계 또는 공용체 유형

컴파일러는 v->a = 10;값에 영향을 줄 수 있으므로 를 최적화하지 않습니다 *some_int_pointer(그리고 10대신 함수가 반환 됩니다 5).


18

A union는 몇 가지 시나리오에서 유용합니다. union커널 용 장치 드라이버 작성과 같은 매우 낮은 수준의 조작을위한 도구 일 수 있습니다.

그 예로는 절개되어 float사용 번호 union(A)의 struct비트 필드와 함께 float. 에 번호를 저장하면 float나중에 float해당 부분의 특정 부분에 액세스 할 수 있습니다 struct. 이 예는 union데이터를보기 위해 다른 각도를 사용 하는 방법을 보여줍니다 .

#include <stdio.h>                                                                                                                                       

union foo {
    struct float_guts {
        unsigned int fraction : 23;
        unsigned int exponent : 8;
        unsigned int sign     : 1;
    } fg;
    float f;
};

void print_float(float f) {
    union foo ff;
    ff.f = f;
    printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);

}

int main(){
    print_float(0.15625);
    return 0;
}

위키 백과에 대한 단 정밀도 설명을 살펴보십시오 . 나는 거기에서 예제와 매직 넘버 0.15625를 사용했다.


union또한 여러 대안이있는 대수 데이터 유형을 구현하는 데 사용될 수 있습니다. O'Sullivan, Stewart 및 Goerzen의 "Real World Haskell"책에서 그 예를 찾았습니다. 차별 조합 섹션 에서 확인하십시오 .

건배!


11

" union "및 " struct "는 C 언어의 구성 입니다. 하나 또는 다른 키워드를 사용하는 경우 다른 코드를 생성 하는 컴파일러 이기 때문에 이들 사이의 "OS 수준"차이를 말하는 것은 부적절 합니다.


11

비 기술적으로 말하는 것은

가정 : 의자 = 메모리 블록, 사람 = 변수

구조 : 인원이 3 명인 경우에는 해당하는 크기의 의자에 앉을 수 있습니다.

노동 조합 : 3 명이 앉는 경우 의자 하나만 앉게되므로 앉을 때 모두 같은 의자를 사용해야합니다.

기술적으로 말하면 다음을 의미합니다.

아래에 언급 된 프로그램은 구조와 연합을 심층적으로 다룹니다.

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

총 MAIN_STRUCT 크기 = bufferaddr의 경우 sizeof (UINT64) + union의 경우 sizeof (UNIT32) + 패딩의 경우 32 비트 (프로세서 아키텍처에 따라 다름) = 128 비트. 구조를 위해 모든 멤버는 메모리 블록을 연속적으로 얻습니다.

Union은 최대 크기 멤버의 메모리 블록을 하나 얻습니다 (여기서는 32 비트). 유니언 내부에 하나 이상의 구조가 있습니다 (INNER_STRUCT) 그 멤버는 총 크기 32 비트 (16 + 8 + 8)의 메모리 블록을 얻습니다. 통합적으로 INNER_STRUCT (32 비트) 멤버 또는 data (32 비트)에 액세스 할 수 있습니다.


좋은 설명입니다. 건배!
Prem

11

예, 구조체와 공용체의 주요 차이점은 언급 한 것과 같습니다. Struct는 멤버의 모든 메모리를 사용하고 Union은 가장 큰 멤버 메모리 공간을 사용합니다.

그러나 모든 차이점은 메모리의 사용량 요구에 달려 있습니다. 우리가 신호를 사용하는 유닉스 프로세스에서 노조의 최상의 사용법을 볼 수 있습니다. 프로세스는 한 번에 하나의 신호에만 작용할 수 있습니다. 따라서 일반적인 선언은 다음과 같습니다.

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

이 경우 프로세스는 모든 신호 중 가장 높은 메모리 만 사용합니다. 그러나이 경우 구조체를 사용하면 메모리 사용량이 모든 신호의 합입니다. 많은 차이를 만듭니다.

요약하면 한 번에 멤버 중 하나에 액세스 할 수있는 경우 Union을 선택해야합니다.


10

당신은 그것을 가지고 있습니다. 그러나 기본적으로 노조의 요점은 무엇입니까?

다른 유형의 동일한 위치 컨텐츠를 넣을 수 있습니다. 당신은 당신이 유니온에 저장 한 것의 유형 을 알아야 합니다 (따라서 종종 struct유형 태그와 함께 넣습니다 ...).

이것이 왜 중요한가? 실제로 공간 확보가 아닙니다. 예, 약간의 비트를 얻거나 패딩을 할 수는 있지만 더 이상 요점이 아닙니다.

형식 안전성을 위해 일종의 '동적 타이핑'을 수행 할 수 있습니다. 컴파일러는 컨텐츠가 다른 의미와 런타임시 해석 방법에 대한 정확한 의미를 가질 수 있음을 알고 있습니다. 다른 유형을 가리킬 수있는 포인터가있는 경우 유니온을 사용해야합니다. 그렇지 않으면 앨리어싱 문제로 인해 코드가 올바르지 않을 수 있습니다 (컴파일러는 "이 포인터만이 유형을 가리킬 수 있으므로 최적화 할 수 있습니다) 액세스를 ... "하고 나쁜 일이 발생할 수 있습니다).


9

구조는 모든 요소의 총 크기를 할당합니다.

공용체는 가장 큰 멤버에게 필요한만큼의 메모리 만 할당합니다.


2
할당 된 공용체 "구조"의 시작 주소에서 모두 시작되도록 공용체 멤버를 서로 "오버레이"하도록 추가 할 수도 있습니다.
Jim Buck

4

구조와 노조의 차이점은 무엇입니까?

지름길은 다음과 같습니다. 차이는 메모리 할당에 있습니다. 설명 : 구조에서 구조 내부의 모든 구성원에 대해 메모리 공간이 작성됩니다. 통합 메모리 공간은 가장 큰 메모리 공간이 필요한 멤버에 대해서만 작성됩니다. 다음 코드를 고려하십시오.

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

구조체와 공용체 안에는 int와 long int의 두 멤버가 있습니다. int의 메모리 공간은 4 바이트이고 long int의 메모리 공간은 32 비트 운영 체제에서 8입니다.

따라서 구조체의 경우 4 + 8 = 12 바이트가 생성되고 8 바이트가 결합됩니다.

코드 예 :

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

참조 : http://www.codingpractise.com/home/c-programming/structure-and-union/


3

특수 유형 대화가 필요할 때 노조 조합을 자주 사용합니다. 노동 조합의 유용성에 대한 아이디어를 얻는 것. c / c 표준 라이브러리는 파일에 짧은 정수를 쓰도록 특별히 설계된 함수를 정의하지 않습니다. fwrite ()를 사용하면 간단한 작업을 위해 과도한 오버 헤드가 발생합니다. 그러나 공용체를 사용하면 짧은 정수의 이진을 한 번에 한 바이트 씩 파일에 쓰는 함수를 쉽게 만들 수 있습니다. 짧은 정수는 2 바이트 길이라고 가정합니다.

예 :

#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}    

putw () i는 짧은 정수로 호출되었지만 putc () 및 fwrite ()를 사용하는 것이 가능했습니다. 그러나 나는 노동 조합이 어떻게 사용될 수 있는지 지배하는 예를 보여주고 싶었다.


3

구조는 서로 다른 유형의 데이터가 존재할 수 있고 모든 사람이 자신의 메모리 블록을 얻는 다른 데이터 유형의 모음입니다.

우리는 일반적으로 변수 중 하나만 한 번에 사용되며 현재 메모리를 최대한 활용하려는 경우 가장 큰 유형과 동일한 하나의 메모리 블록 만 가져 오기 때문에 공용체를 사용했습니다.

struct emp
{
    char x;//1 byte
    float y; //4 byte
} e;

그것이 얻는 총 메모리 => 5 바이트

union emp
{
    char x;//1 byte
    float y; //4 byte
} e;

총 메모리 = 4 바이트


2

유니온은 아래에 주어진 바이트 순서 함수를 작성하는 동안 편리합니다. 구조체로는 불가능합니다.

int main(int argc, char **argv) {
    union {
        short   s;
        char    c[sizeof(short)];
    } un;

    un.s = 0x0102;

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.

1

Union은 Union이 다른 것보다 반복하므로 구조체와 다릅니다. 구조체가 겹치거나 재정의하지 않고 차례로 정의하는 동안 동일한 메모리를 재정의합니다.

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