[2-11] 공용체

공용체는 동일한 메모리 영역을 여러 자료형이 공유하는 사용자 정의 자료형입니다. 선언 방법은 다음과 같습니다.

union 자료명 {
자료형 1;
자료형 2;
.
.
자료형 N;
};
  • 자료명 : 공용체의 자료 이름입니다.
  • 자료형(1~N) ; 메모리를 공유할 자료형들입니다. 같은 자료형이어도 되고, 서로 달라도 됩니다.

공용체는 일반적으로 다음과 같은 목적으로 사용합니다.

  • 더 큰 자료형의 개별 바이트에 접근할 때
  • 가변 형식의 입력을 주고받을 때
  • 저장 공간 절약 목적

공용체 크기

예를 들어 혈액형과 나이 중 하나만 담는 구조체를 만들면 다음과 같습니다.

union myInfo {
    char bloodType;
    int age;
};

공용체 myInfo는 총 4바이트 크기입니다. 두 멤버 변수 중 제일 큰 변수의 크기를 취하기 때문입니다. 다음 그림을 보면 bloodType의 영역은 0x00번지, age는 0x00부터 0x03번지입니다.

그림으로 살펴보죠.

[그림] 메모리에 할당된 구조체 myInfo

메모리에 할당된 구조체 myInfo

정말 그런지 코드로도 살펴보겠습니다.

#include <stdio.h>

union myInfo {
    char bloodType;
    int age;
};


int main(void) {
	
	printf("%d\n", sizeof(union myInfo));
	
	return 0;
}

[결과]

4

역시나 예상대로 크기가 4바이트로 출력되는군요!

공용체 초기화하기

공용체 변수를 초기화하는 방법 역시 구조체와 같습니다.

  1. 변수 선언 이후에 멤버 변수마다 초기화하기
  2. 변수 선언 때 초기화하기

변수 선언 이후에 멤버 변수마다 초기화하하는 방법을 살펴보겠습니다. 공용체 변수 할당과 멤버 변수 접근은 방법은 다음과 같습니다.

union myInfo myU;
myU.age = 'A';

접근법은 구조체에서와 마찬가지로 점 연산자를 사용합니다. 이미 구조체에서 접해봤으니 어렵지 않을 겁니다.

두 번째 방법인 변수 선언 때 초기화를 해보겠습니다.

union myInfo myU ={'A'};

구조체와 마찬가지로 초기화하지 않은 영역은 0으로 패딩됩니다.

정말 그렇게 동작하는지 예제로 확인해보시죠.

#include <stdio.h>

union myInfo {
    char bloodType;
    int age;
};


int main(void) {
	union myInfo myU;
	union myInfo yourU = {'B'};
	
	myU.bloodType = 'A';
	
	printf("myU.bloodType = %c, myU.age = %i\n", myU.bloodType, myU.age);
	printf("yourU.bloodType = %c, yourU.age = %i\n", yourU.bloodType, yourU.age);

	return 0;
}

[결과]

myU.bloodType = A, myU.age = 65
yourU.bloodType = B, yourU.age = 66

두 공용체의 bloodType값이 예상대로 각각 A와 B가 입력되었네요. 두 공용체 모두 bloodType만 초기화했습니다. 즉 4바이트에서 1바이트만 우리가가 초기화한 거죠. 나머지 영역은 자동으로 0으로 패딩되어 age값이 각각 A와 B의 10진수값으로 출력되었습니다.

공용체 사용하기

그럼 어떻게 동작하는지 공용체 myInfo를 사용해보겠습니다. 입력으로는 A 그다음에는 33을 넣겠습니다.

#include <stdio.h>

union myInfo {
    char bloodType;
    int age;
};


int main(void) {
	union myInfo myU;
	
	scanf("%c", &myU.bloodType);
	printf("%c\n", myU.bloodType);
	
	scanf("%i", &myU.age);
	printf("%i\n", myU.age);
	
	printf("%c\n", myU.bloodType);
	
	return 0;
}

[출력]

A
33
!

A와 33의 출력 결과는 당연히 예측을 하셨을 겁니다. 그런 마지막 출력이 A가 아니라 !(느낌표)네요. 공용체는 멤버 변수들이 메모리 공간을 공유하는데, 처음에 A를 쓰고 이어서 33을 덮어 썼기 때문입니다. 그후 myU.bloodType를 출력했을 때 33의 아스키값인 느낌표를 출력한 겁니다.

이 과정을 그림으로 살펴보면 다음과 같습니다.

[그림] 위 예제에서 메모리 값의 변화

메모리 값의 변화

앞서 다룬 예제의 결과에서 뭔가 의미 하나를 뽑아볼까요?

최종 결과가 0x21의 아스키 코드값인 !(느낌표)인 걸보면 MSB가 오른쪽에 있다는 이야깁니다. 고로 리틀엔디안 방식이라는 거죠.

이름 없는 공용체

공용체를 이름 없이 사용할 수도 있습니다. 바로 구조체 안에서 사용될 때입니다. 예제 코드를 한 번 살펴보죠.

struct score{
	char name[20];
	int age;
	union {
		int korean;
		int english;
		int math;
	}
};

이때 공용체 멤버 변수로의 접근 방법은 구조체 멤버 변수로의 접근 방법과 같습니다.

다음과 같이 구조체 변수 myU가 있다고 합니다.

struct score myU;

구조체 변수 아래 cahr형 배열과 int형 그리고 구조체 안에 세 int형 변수가 있습니다. 구조체 안의 공용체 멤버변수 역시 다음과 같이 점 연산자 하나만 사용하면 접근할 수 있습니다.

myU. age = 33;
myU. korean = 99;

정말 그런지 돌아가는 예제로 확인해봅시다.

#include <stdio.h>

struct score{
	char name[20];
	int age;
	union {
		int korean;
		int english;
		int math;
	};
};

int main(void) {
	struct score majorInfo = {"Guan Yu", 33, 100};

	printf("majorInfo.name = %s\n", majorInfo.name);
	printf("majorInfo.age = %d\n", majorInfo.age);
	printf("majorInfo.english = %d\n", majorInfo.english);
	
	return 0;
}

[결과]

majorInfo.name = Guan Yu
majorInfo.age = 33
majorInfo.english = 100

어때요? 관우에게 빈 말은 없죠?