일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Windows
- 배열
- 김성엽
- Win32
- 포인터
- Desktop
- Programming
- tipssoft
- Tips강좌
- Direct2D
- 프로그래밍
- 지식나눔강좌
- 백준
- doit코틀린프로그래밍
- 연산자
- 문법
- VS ERROR
- c++
- CS
- c
- 함수
- 이지스퍼블리싱
- Javascript
- Visual Studio
- Tips프로그래밍강좌
- 알고리즘
- Kotlin
- c#
- 티스토리
- 리뷰
- Yesterday
- Today
- Total
F.R.I.D.A.Y.
공용체(union) part1. default 본문
하나의 공간을 여러 용도로 사용하는 경우가 있습니다. C언어에도 그러한 공간을 만들 수 있도록 문법을 제공하고 있는데요. 오늘 시간에는 공용체(union, 유니온) 문법에 대해 알아봅니다.
# 구조체를 먼저 배우고 오면 더욱 좋습니다.
공유하는 공간
회의장이 있다고 생각해봅시다. 이 회의장은 넓어서 행사를 개최할 때도 사용하고 여러 사람들이 모여 아이디어를 펼치는 아이디어룸으로 사용할 수도 있습니다. 어떤 시안에 대해 회의를 할 때도 사용하기도 하죠. 학교에선 다목적 강당으로 생각해도 좋을 것 같습니다.
이 회의장을 하나의 용도로만 사용해야 한다면 다른 용도를 위한 공간을 계속해서 만들어야 하니 비용이 많이 들 것입니다.
공용체(union) 선언하기
공용체 문법은 아래와 같이 기본 구성을 가지고 있습니다.
union data{
int a;
char b;
short c;
float d;
};
모양이 구조체 문법과 비슷합니다. 선언함에 있어서도 구조체의 선언 방법과 동일합니다. 그러나 구조체 문법과는 차이가 있습니다. 그 차이를 지금부터 설명합니다.
구조체와의 차이
구조체는 특정 대상의 정보를 하나의 그룹으로 표현하는데 적합한 문법이라 했습니다. 그러다 보니 구조체의 공간은 멤버 변수의 크기 총합과 같거나 큽니다[# 구조체 정렬에 의해 발생하는 것으로 자세한 내용은 다음 포스트 참고.]. 그러나 공용체 문법의 경우에는 다릅니다. 하나의 공간을 여러 변수가 함께 사용하기 때문에 공용체의 크기는 멤버 변수 중에서 가장 큰 크기의 자료형이 공용체의 크기가 됩니다.
union data{
int a;
char b;
short c;
float d;
};
위에 선언된 공용체 'data'의 크기는 얼마일까요? 가장 큰 크기인 것은 4Byte의 int(혹은 float)입니다. 따라서 union data의 크기는 4Byte로 고정됩니다. 실제로 맞는지 알아보겠습니다.
#include <stdio.h>
union data{
int a;
char b;
short c;
float d;
};
int main(void){
union data t;
printf("%lu\n", sizeof(t));
return 0;
}
가장 큰 자료형인 int(혹은 float) 크기를 따라 결정되었습니다. union data가 선언될 때 메모리에는 아래와 같이 공간을 공유하도록 만들어집니다.
공간을 공유하다 보니 어느 하나의 멤버를 사용(쓰기)했다면 다른 멤버로 작성한 값이 변동됩니다. 값의 오염이 이루어지는 셈이죠. 이 점은 굉장히 중요한 것으로, 코드로 알아보겠습니다.
# 샘플 코드
#include <stdio.h>
union age{
char people;
short tree;
int asteroid;
};
int main(void){
int input;
union age obj = {0};
while(1){
printf("어떤 대상의 나이를 입력합니까?\n");
printf("1. people 2. tree 3.asteroid etc.exit\n");
printf("입력: ");
scanf("%d", &input);
if(input == 1){
printf("사람의 나이: ");
scanf("%hhd", &obj.people);
}else if(input == 2){
printf("나무의 수령: ");
scanf("%hd", &obj.tree);
}else if(input == 3){
printf("소행성의 나이: ");
scanf("%d", &obj.asteroid);
}else{
break;
}
printf("사람 : %d\n", obj.people);
printf("나무 : %d\n", obj.tree);
printf("소행성: %d\n", obj.asteroid);
}
return 0;
}
샘플 코드에서 사람을 선택하고 값을 입력해보겠습니다.
사람뿐 아니라 나무, 소행성의 나이 역시 15가 출력됨을 알 수 있습니다. 이렇게 같은 메모리 영역을 공유한다는 것을 알 수 있습니다. 나무의 수령으로 150을 입력해보겠습니다.
나무와 소행성의 나이는 150으로 나왔는데 사람은 -106으로 나왔습니다. 어째서 이 문제가 발생할까요? 원인은 멤버 변수의 크기에 있습니다.
우리는 사람은 char, 나무는 short, 소행성은 int[# 물론 우주적으론 부족한 값이지만 :)]로 설정했습니다. char은 8비트 중 7번 비트[# MSB, 비트 번호를 읽는 법은 이 문서 참고]를 부호 비트로 사용합니다. 150을 이진수로 변환해보겠습니다.
1001 0110
MSB 영역을 침범하게 됩니다. char로 변환했을 때, 부호 비트가 1이니 음수이고 음수의 최소 값인 -128에 0001 0110[# 22]를 더해줍니다. 그러면 -128 + 22 = -106이 나오게 됩니다.
공용체의 사용
그럼 이렇게 멤버 변수를 하나밖에 사용할 수 없는데 이런 문법을 어디에 사용하느냐? 궁금해하실 수 있습니다. 물론 일반적인 프로그래밍에서는 거의 사용하지 않습니다.
어떤 변수에 값을 비트 단위로 넣어야하고 추출해야하는 문제가 있습니다. 그러나 우리는 비트 연산자를 잘 모릅니다. 이럴 때 공용체와 구조체를 이용해 편리하게 비트 단위로 값을 추출할 수 있습니다.
union data{
struct d{
char _0:1; // LSB
char _1:1;
char _2:1;
char _3:1;
char _4:1;
char _5:1;
char _6:1;
char _7:1; // MSB
} bit;
char origin;
};
위와 같은 유니온 자료형을 선언하고 이용한다고 가정합니다.
#include <stdio.h>
union data{
struct d{
char _0:1; // LSB
char _1:1;
char _2:1;
char _3:1;
char _4:1;
char _5:1;
char _6:1;
char _7:1; // MSB
} bit;
char origin;
};
int main(void){
union data t;
t.origin = 96;
printf("%hhd %hhd %hhd %hhd (-) %hhd %hhd %hhd %hhd\n",
(char)t.bit._0, (char)t.bit._1, (char)t.bit._2, (char)t.bit._3,
(char)t.bit._4, (char)t.bit._5, (char)t.bit._6, (char)t.bit._7);
printf("%hhd\n", t.origin);
return 0;
}
실제 값을 t의 멤버 변수 origin에 대입하게 되면 공용체의 특성으로 인해 그 내부 변수인 struct d 자료형의 bit 멤버 변수가 t의 멤버 변수 origin이 가진 값을 공유하게 됩니다. 이미지로 보면 다음과 같습니다.
struct d 자료형의 bit 변수 안에 있는 멤버 변수들이 각 비트에 접근하는 변수인 것입니다.
물론 이 경우에는 구조체의 비트 필드 연산자[# :]를 사용하였으나, 1 Byte 단위로 데이터를 추출하고자 할 때 함수나 전처리 지시자를 이용한 매크로 함수를 작성하지 않고도 접근하는 경우에도 사용할 수 있습니다.
들은 바에 의하면 극한의 메모리 최적화가 필요한 곳[# 임베디드 시스템 정도]에서 종종 사용한다고 합니다. 물론 리눅스 커널 소스코드에서도 심심찮게 살펴볼 수 있습니다.
알고 있는 문법이지만 수 년간의 프로그래밍 생활동안 유니온 문법은 타인에게 소개하려고 사용한 경우를 빼곤 손에 꼽네요.
더 읽어보기
구조체에서 주로 사용하기 때문에 구조체 파트로 설명했지만, 공용체에서도 사용할 수는 있습니다. 물론 사용하면 의미가 있겠느냐마는요.
# index
'DEV > C C++' 카테고리의 다른 글
포인터(pointer) part3. 함수 포인터 (0) | 2020.01.30 |
---|---|
구조체(struct) part2. 비트 필드 (0) | 2020.01.29 |
포인터의 크기 (0) | 2020.01.26 |
포인터 배열? 배열 포인터? (0) | 2020.01.26 |
다차원 배열의 요소 값 교환하기 (0) | 2020.01.25 |