공용체와 구조체는 선언과 사용하는 방법 면에서 같은 형태를 가지고 있습니다. 그러나 구조체의 경우 모든 멤버들이 각각 독립된 메모리 공간을 할당 받는 반면 공용체는 멤버들 중 가장 큰 사이즈의 타입만큼의 메모리 공간만 할당 받고 그 메모리 공간을 모든 멤버들이 함께 공유하게 됩니다. 때문에 멤버변수 중 하나를 변경하면 나머지 모든 변수의 값도 함께 변하게 됩니다. 멤버 접근방식과 포인터 멤버를 가졌을 때, 공용체 배열이나 포인터에 대한 접근 등 나머지 모든 특징은 구조체와 같습니다. 그리고 공용체를 초기화 할 때는 반드시 하나의 멤버만 초기화해야 합니다.
예전의 제가 C언어를 처음 배울 당시 대부분 책에는 공용체를 남자와 여자의 정보를 각각 저장할 때 서로 달라 지는 멤버(주로 자녀 수와 수염여부)를 통합하여 공용체로 선언한다고 소개하곤 했습니다.
하지만 현재 제가 아는 한 실제 개발 필드에서 사용되고 있는 공용체는 그 보다 더 멋진 일을 하고 있습니다.
예를 들어 네트워크 주소를 표현할 때 일반적으로 165.213.77.52와 같은 문자열 주소를 사용하지만 내부적으로는 32bit long형 주소 값을 사용하고 있는데 이럴 때 공용체는 아주 멋진 해결책이 될 수 있습니다.
뿐만 아니라 사이트에 회원 등록 시 입력해야 할 주민등록번호나 외국인등록번호도 공용체로 저장하기도 합니다. 이렇게 서로 공존하지 않을 상호 배타적인 정보를 동일한 메모리 공간에 함께 저장할 수 있는 방법을 제공하는 것이 바로 공용체입니다.
게다가 공용체를 잘 활용하면 대부분의 교재에 정수형 데이터로만 소개하고 있는 연결 리스트도 타입에 관계없이 동작하는 범용 연결 리스트를 만들 수 있게 됩니다.
그리고 아래와 같이 공용체를 만들면 같은 4바이트 메모리 공간을 여러 가지 형태로 제어할 수 있게 됩니다.
하지만 공용체의 가장 큰 문제점은 어떤 멤버의 값이 설정되어 있는지 전혀 알 수 없다는 것입니다. 그렇기 때문에 공용체를 단독으로 사용하지 않고 구조체의 멤버로 메타 데이터와 함께 묶어서 사용하는 것이 일반적입니다.
typedef union data {
int word ;
short hword[2] ;
// char byte[4] ;
char byte[5] ;
// 이렇게 사용하면 byte[4]에 어떤 멤버 값으로 설정했는지 지정할 수도 있죠.
} Data ;
// 여기에 바로 앞에서 배운 구조체 비트필드를 포함시키면 비트제어까지 가능해 집니다.
이름없는 구조체와 공용체 멤버
어떤 공용체가 다른 공용체를 멤버로 포함하는 경우는 거의 없지만 공용체 안에 구조체를 포함하거나 또는 구조체 안에 다른 구조체나 공용체를 멤버로 포함하는 것은 프로그래밍에서 자주 사용합니다. 이때 앞에서 이미 선언된 구조체나 공용체 타입을 그대로 가져다 사용하기도 하지만 내부에 따로 직접 선언하기도 합니다. 이때 포함되는 구조체나 공용체의 이름을 주지 않을 수 있는데, 이때 그 안에 선언될 멤버의 이름이 구조체나 공용체 영역 안에서 유일하지 않으면 문법 에러가 발생합니다. 뿐만 아니라 그 이름 없는 선언은 필요한 다른 곳에도 똑같이 모든 멤버를 새로 선언해야 합니다. 하지만 프로그램에서 오직 하나뿐인 선언이라면 이렇게 이름 없이 포함하는 방법을 사용하여 멤버 변수이름을 적지 않고도 바로 내부 멤버를 직접 접근할 수도 있습니다.
일반적인 구조체
//선언
struct point {
int x ;
int y ;
} ;
// 밖에 따로 먼저 선언
struct coord {
struct point XY ;
int radius ;
// int x ; 멤버 선언 가능
// Circle.x와 Circle.XY.x로
// 구별하여 사용 가능
} Circle ;
// 중간에 멤버 변수 이름을 // 적어야 한다.
Circle.XY.x ;
Circle.XY.y ;
Circle.radius ;
이름없는 구조체
//선언
struct coord {
int radius ;
// 안에 포함하여 선언
struct {
int x ;
int y ;
} ;
// int x ; 멤버 선언 불가능
// 멤버구별이 안되기 때문에 문법 에러
} Circle ;
// 멤버 변수이름 없이 내부멤버 직접 접근
Circle.x ;
Circle.y ;
Circle.radius ;
우리가 앞에서 이미 선언한 struct coord 구조체에서 다른 구조체가 포함된 형태가 아닌 이름없는 구조체 선언을 사용하면 다음과 같습니다.
gcc로 컴파일 했을 때 두 코드의 실행 파일 사이즈는 동일하며, 단지 같은 선언을 반복해야 하는 불편함이 있습니다.