공용체 사용하기

  • 10 minutes to read

공용체는 특별한 데이터 타입으로, 동일한 메모리 영역을 공유하는 다양한 데이터 타입을 저장할 수 있는 특성을 갖고 있습니다. 구조체와 유사하나 한 번에 하나의 멤버만 저장할 수 있습니다. 이는 메모리 공간을 효율적으로 활용할 수 있게 해줍니다. 다양한 공용체 사용 사례를 통해 이를 이해해봅시다.

공용체와 구조체는 메모리 사용 방식에서 중요한 차이가 있습니다. 구조체는 모든 멤버 변수에 각각의 메모리 공간을 할당하지만, 공용체는 모든 멤버가 동일한 메모리 공간을 공유합니다. 그러므로, 공용체의 메모리 크기는 그 안에 있는 가장 큰 데이터 타입에 맞게 설정됩니다. 이를 그림으로 표현하면 다음과 같습니다:

그림: 공용체의 메모리 크기

공용체의 메모리 크기

이 그림에서 볼 수 있듯이, 공용체는 int와 char 타입이 동일한 메모리 공간을 공유하여 이 공간의 크기는 가장 큰 데이터 타입인 int의 크기와 동일합니다. 이러한 특성 때문에, 공용체는 메모리를 효율적으로 사용할 수 있지만, 한 시점에 하나의 멤버 값만 유지할 수 있다는 점에 유의해야 합니다. 한 멤버의 값을 변경하면, 이는 다른 멤버들에게도 영향을 미치게 됩니다.

동일 메모리 공간을 공유하는 공용체 멤버들

공용체의 핵심 특성 중 하나는 여러 변수가 동일한 메모리 공간을 공유한다는 점입니다. 이는 공용체를 사용함에 있어 중요한 이해 요소이며, 아래 예제는 이러한 특성을 구체적으로 보여줍니다.

파일명: union_same.c

#include <stdio.h>

union Same
{
    int x;
    int y;
};

int main(void)
{
    union Same s;
    s.x = 10;
    s.y = 20;
    printf("s의 크기: %ld\n", sizeof(s));
    printf("s.x: %d, s.y: %d\n", s.x, s.y);

    return 0;
}

출력 결과

s의 크기: 4
s.x: 20, s.y: 20

이 예시에서, s.x와 s.y는 동일한 메모리 공간을 공유합니다. 따라서 s.y에 값을 할당하면 s.x의 값도 동일하게 변경됩니다.

다양한 데이터 타입과 공용체

정수, 문자 등 다양한 데이터 타입을 가진 공용체를 생성할 수 있습니다.

파일명: union_share.c

#include <stdio.h>

union Share
{
    int x;
    char ch;
};

int main(void)
{
    union Share s;
    s.x = 65;
    printf("s.x: %d, s.ch: %c\n", s.x, s.ch);

    return 0;
}

출력 결과

s.x: 65, s.ch: A

이 예시에서는 정수 65를 저장하고 이 값을 문자로도 출력하는 것을 보여줍니다. 정수 65의 ASCII 코드는 'A'입니다.

공용체의 활용

공용체를 사용하여 다양한 자료형을 저장하고 출력하는 여러 가지 방법이 있습니다.

파일명: union_data.c

#include <stdio.h>
#include <string.h>

// 공용체의 정의
union Data {
    int i;
    float f;
    char str[20];
};

int main(void) {
    union Data data;

    // i 멤버에 값 할당
    data.i = 10;
    printf("data.i : %d\n", data.i);

    // f 멤버에 값 할당
    data.f = 220.5;
    printf("data.f : %f\n", data.f);

    // str 멤버에 값 할당
    strcpy(data.str, "C Language");
    printf("data.str : %s\n", data.str);

    return 0;
}

출력 결과

data.i : 10
data.f : 220.500000
data.str : C Language

이 코드에서 Data는 공용체의 정의이며, data 변수는 Data 타입의 공용체입니다. data 변수는 i, f, str 중 하나의 멤버만 가질 수 있습니다.

별칭(alias)을 사용한 공용체 정의

공용체를 사용할 때 별칭(alias)을 사용하여 코드를 보다 간결하게 작성할 수 있습니다.

파일명: union_alias.c

#include <stdio.h>
#include <string.h>

union Data {
  int i;
  float f;
  char str[20];
};

typedef union Data DataType;

int main() {
  DataType data;

  data.i = 10;
  printf("data.i: %d\n", data.i);

  data.f = 220.5;
  printf("data.f: %f\n", data.f);

  strcpy(data.str, "C programming");
  printf("data.str: %s\n", data.str);

  return 0;
}

출력 결과

data.i: 10
data.f: 220.500000
data.str: C programming

이 코드에서는 Data 공용체의 별칭인 DataType을 사용하여 공용체를 정의하고, 이를 사용하여 data 변수에 값을 저장하고 출력하고 있습니다.

엔디언(Endianness) 확인하기

엔디언(Endianness)은 데이터를 바이트 단위로 저장하는 순서를 말합니다. 주로 빅 엔디언(Big Endian)과 리틀 엔디언(Little Endian) 두 가지 방식이 사용됩니다. 공용체를 이용하면, 시스템의 엔디언을 확인하는 것이 가능합니다.

코드: endian_check.c

#include <stdio.h>

union endian
{
    unsigned int num;
    char x[4];
};

int main(void)
{
    union endian temp;
    temp.num = 0x01020304;

    printf("%d\n", temp.x[0]);  // 4가 출력되면 리틀 엔디언, 1이 출력되면 빅 엔디언
    return 0;
}
4

이 예제에서는 0x01020304라는 4바이트의 값을 공용체에 저장하고, 첫 번째 원소를 출력합니다. 리틀 엔디언 방식을 사용하는 시스템에서는 4가 출력되고, 빅 엔디언 방식을 사용하는 시스템에서는 1이 출력됩니다. 이렇게 공용체를 이용하여 시스템이 빅 엔디언 방식인지 리틀 엔디언 방식인지 판단할 수 있습니다.

공용체와 구조체의 차이

공용체와 구조체는 비슷해 보일 수 있지만, 주요 차이점이 있습니다.

구조체는 모든 멤버가 메모리 공간을 독립적으로 가집니다. 예를 들어, 정수와 실수를 멤버로 가지는 구조체를 선언하면, 구조체의 크기는 두 멤버의 크기 합이 됩니다. 이렇게 각 멤버는 독립적인 메모리 공간을 가지며, 한 멤버의 값을 변경해도 다른 멤버에는 영향을 미치지 않습니다.

반면, 공용체는 모든 멤버가 동일한 메모리 공간을 공유합니다. 동일한 메모리 공간을 사용하기 때문에, 한 멤버의 값을 변경하면 다른 멤버의 값도 영향을 받습니다. 공용체의 크기는 가장 큰 멤버의 크기와 같습니다.

아래는 이 차이를 보여주는 예제 코드입니다.

코드: struct_vs_union.c

#include <stdio.h>

struct S
{
    int i;
    float f;
};

union U
{
    int i;
    float f;
};

int main(void)
{
    struct S s;
    union U u;

    printf("구조체의 크기: %lu바이트\n", sizeof(s));  // 출력: 구조체의 크기: 8바이트
    printf("공용체의 크기: %lu바이트\n", sizeof(u));   // 출력: 공용체의 크기: 4바이트

    return 0;
}
구조체의 크기: 8바이트
공용체의 크기: 4바이트

이 코드에서 S는 구조체의 정의이고, U는 공용체의 정의입니다. S 구조체의 크기는 int와 float 두 멤버의 크기 합인 8바이트가 되지만, U 공용체의 크기는 int와 float 중 큰 멤버의 크기인 4바이트가 됩니다. 이 차이는 구조체와 공용체가 메모리를 다루는 방식의 차이를 보여줍니다.

포인터를 이용한 공용체 접근

공용체는 포인터를 이용해서도 접근할 수 있습니다. 아래의 코드는 이를 보여주는 예시입니다.

코드: union_pointer_access.c

#define _CRT_SECURE_NO_WARNINGS     // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

// 공용체 정의
union Box
{
    short candy;
    float snack;
    int num;
};

int main(void)
{
    union Box* b1 = malloc(sizeof(union Box)); // 공용체 포인터 선언, 메모리 할당

    printf("공용체의 전체 크기: %d바이트\n", sizeof(union Box)); // 8: 공용체의 전체 크기는 가장 큰 자료형의 크기

    b1->candy = 10;
    printf("candy의 값: %d\n", b1->candy); // candy의 값: 10

    b1->snack = 3.14f;
    printf("snack의 값: %.2f\n", b1->snack); // snack의 값: 3.14

    b1->num = 100;
    printf("num의 값: %d\n", b1->num); // num의 값: 100

    free(b1); // 동적 메모리 해제

    return 0;
}
공용체의 전체 크기: 4바이트
candy의 값: 10
snack의 값: 3.14
num의 값: 100

이 코드에서는 malloc을 사용하여 공용체 Box의 크기만큼의 메모리를 동적으로 할당합니다. 공용체 Box에는 candy, snack, num이라는 세 개의 멤버가 있으며, 이들 각각에 값을 할당하고 출력해봅니다. 이를 통해 공용체의 멤버들이 동일한 메모리 공간을 공유함을 알 수 있습니다. 마지막으로, free 함수를 사용하여 동적으로 할당한 메모리를 해제합니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com