맨위로가기

공용체 (컴퓨터 과학)

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

공용체는 컴퓨터 과학에서 여러 데이터 유형을 저장할 수 있는 데이터 구조를 의미하며, 저장 공간을 공유하여 메모리를 절약하는 데 사용된다. C, C++, ALGOL 68, COBOL, Pascal, PL/I, Rust, C#, PHP, Python, TypeScript 등 다양한 프로그래밍 언어에서 지원하며, 구현 방식과 기능에 차이가 있다. C/C++에서는 `union` 키워드를 사용하여 정의하며, 구조체와 유사한 문법을 갖지만 메모리 주소를 공유한다는 점이 특징이다. Rust는 태그 유니온과 비태그 유니온을 모두 지원하며, C#은 명시적인 레이아웃을 지정한 구조체를 통해 공용체와 유사한 동작을 구현할 수 있다. 공용체는 임베디드 시스템, 시스템 프로그래밍, 네트워크 프로그래밍 등 다양한 분야에서 활용된다.

2. 공용체의 개념

공용체(union)C 언어에서 사용되는 자료형으로, 여러 멤버(필드)를 가지지만 한 번에 하나의 멤버만 값을 저장할 수 있다는 점에서 구조체와 구별된다. 이러한 특징은 타입의 형식적인 정의에서 유래했는데, 타입을 해당 타입이 가질 수 있는 모든 값의 집합으로 간주하면, 공용체 타입은 구성하는 타입들의 수학적 합집합과 같다.

공용체는 멤버 중 어느 것이든 가질 수 있기 때문에, 수학적 합집합처럼 중복을 제거한다. 만약 공용체의 둘 이상의 필드가 단일 공통 값을 가질 수 있다면, 값만으로는 어떤 필드가 마지막으로 쓰였는지 알 수 없다.

C 언어에서 공용체는 모든 멤버의 오프셋이 0인 구조체와 유사하게, `struct` 대신 `union` 키워드를 사용하여 선언된다. 멤버 접근은 `.` 또는 `->` 연산자를 사용한다. 공용체의 크기는 가장 큰 멤버를 기준으로 결정되며, 패딩이 추가될 수 있다.[16][17]

```c

/* 공용체 MyUnion1을 정의 */

union MyUnion1 {

double x;

int y;

char z[10];

/* double, int, char[10] 중 하나를 저장할 수 있다 */

};

```

위 코드에서 `MyUnion1`은 `double`, `int`, `char[10]` 중 하나의 타입으로 값을 저장할 수 있는 공용체이다. 공용체의 초기화는 처음에 선언된 멤버의 유형으로 수행해야 한다.

C99까지는 익명 구조체 및 공용체가 허용되지 않았지만, C11에서는 허용되었다.[16][17]

C++에서 공용체는 클래스의 일종으로, 멤버 함수를 가질 수 있지만 상속, 정적 멤버, POD가 아닌 클래스의 객체형 멤버, 참조형 멤버는 가질 수 없다는 제약이 있다.[18] C#에서는 `System.Runtime.InteropServices.StructLayoutAttribute`를 사용하여 공용체와 유사한 동작을 하는 구조체를 정의할 수 있다.

공용체의 주요 기능 중 하나는 더 작은 데이터 요소를 더 큰 요소에 매핑하는 것이다. 예를 들어, 4바이트와 32비트 정수로 구성된 데이터 구조는 부호 없는 64비트 정수와의 공용체를 형성하여 비교 등의 목적으로 더 쉽게 접근할 수 있도록 한다.

2. 1. 공용체와 구조체의 비교

C 언어에서 공용체는 모든 멤버의 오프셋이 0 (즉, 첫 번째 바이트부터 시작)인 구조체와 유사하다. 선언 시 예약어 `struct` 대신 `union`을 사용한다는 점을 제외하면 구조체와 동일한 구문으로 선언 및 정의된다. 멤버 접근 역시 `.` 연산자 또는 `->` 연산자를 통해 구조체와 같은 방식으로 이루어진다. 공용체의 전체 크기는 멤버 중 가장 큰 것을 저장할 수 있을 만큼 결정되며, 패딩이 추가될 수 있다.[16][17]

구조체는 각 멤버가 독립적인 메모리 공간을 갖는 반면, 공용체는 모든 멤버가 같은 메모리 공간을 공유한다. 따라서 공용체의 크기는 가장 큰 멤버의 크기와 같다.

C99까지는 익명의 구조체 및 공용체가 허용되지 않았으나, C11부터 허용되었다.

C++의 공용체는 클래스(및 구조체)의 일종으로, C의 공용체 기능에 멤버 함수 추가 등 몇 가지 기능이 더해졌다. 그러나 일반적인 클래스(및 구조체)에 비해 다음과 같은 제약이 따른다.[18]

  • 상속 불가:
  • 기저 클래스를 가질 수 없다.
  • 공용체로부터 파생될 수 없다.
  • 가상 함수를 가질 수 없다.
  • 정적 멤버를 가질 수 없다.
  • POD가 아닌 클래스의 객체형 멤버를 가질 수 없다.
  • 참조형 멤버를 가질 수 없다.


C#에서는 공용체 전용 구문이 없지만, `System.Runtime.InteropServices.StructLayoutAttribute`를 통해 명시적 레이아웃을 지정한 구조체에서 공용체와 유사한 동작을 구현할 수 있다.

2. 2. 태그 없는 공용체 (Untagged Union)

C 언어C++에서 태그 없는 공용체는 구조체(struct)와 거의 동일하게 표현되지만, 각 데이터 멤버는 동일한 메모리 주소에 위치한다. 구조체와 마찬가지로 데이터 멤버는 기본 값이 아니어도 되며, 실제로 구조체나 다른 공용체일 수도 있다. C++(C++11 이후)는 또한 완전한 생성자/소멸자 및/또는 복사 생성자 또는 비자명 복사 할당 연산자를 갖는 모든 유형의 데이터 멤버를 허용한다. 예를 들어 표준 C++ string을 공용체의 멤버로 갖는 것이 가능하다.

공용체의 주요 사용법은 서로 다른 데이터 유형으로 공통 위치에 접근할 수 있도록 하는 것이다. 예를 들어 하드웨어 입/출력 접근, 비트 필드 및 워드 공유 또는 타입 펀닝이 있다. 공용체는 또한 낮은 수준의 다형성을 제공할 수 있다. 그러나 타입 검사가 없으므로, 서로 다른 컨텍스트에서 적절한 필드에 접근하는 것은 프로그래머의 책임이다. 공용체 변수의 관련 필드는 일반적으로 다른 변수(아마도 외부 구조체에)의 상태에 의해 결정된다.

일반적인 C 프로그래밍 관용구 중 하나는 공용체를 사용하여 C++에서 `reinterpret_cast`라고 부르는 것을 수행하는 것이다. 이는 공용체의 한 필드에 할당하고 다른 필드에서 읽는 방식으로 수행되며, 값의 원시 표현에 의존하는 코드에서 사용된다. 실용적인 예는 IEEE 표현을 사용하여 제곱근을 계산하는 방법이다. 그러나 이것은 일반적으로 공용체를 안전하게 사용하는 방법은 아니다.

공용체의 사용상의 제약 때문에, 태그 없는 공용체는 일반적으로 C 언어와 같이 타입이 없는 언어에서나 타입 안전성이 없는 방식으로만 제공된다. 태그 없는 공용체는 단순한 태그된 공용체보다 데이터 타입 태그를 저장하기 위한 공간이 필요하지 않다는 장점이 있다.

"공용체(union)"라는 이름은 타입의 형식적인 정의에서 유래했다. 만약 타입을 해당 타입이 가질 수 있는 모든 값의 집합으로 간주한다면, 공용체 타입은 구성하는 타입들의 수학적 합집합과 같다. 공용체는 필드 중 어느 것이든 가질 수 있기 때문이다. 또한 수학적 합집합은 중복을 제거하므로, 공용체의 둘 이상의 필드가 단일 공통 값을 가질 수 있다면, 값만으로는 어떤 필드가 마지막으로 쓰였는지 알 수 없다.

그러나 공용체의 유용한 프로그래밍 기능 중 하나는 더 쉬운 조작을 위해 더 작은 데이터 요소를 더 큰 요소에 매핑하는 것이다. 예를 들어 4바이트와 32비트 정수로 구성된 데이터 구조는 부호 없는 64비트 정수와의 공용체를 형성할 수 있으며, 따라서 비교 등의 목적으로 더 쉽게 접근할 수 있다.

3. 다양한 프로그래밍 언어에서의 공용체

ALGOL 68은 태그가 붙은 공용체를 지원하며, 런타임에 구성 요소 유형을 구별하고 추출하기 위해 case 절을 사용한다. 다른 공용체를 포함하는 공용체는 모든 구성 요소 가능성의 집합으로 취급되며, 문맥상 필요할 경우 공용체는 자동으로 더 넓은 공용체로 변환된다. 공용체는 런타임에 구별할 수 있는 값을 명시적으로 포함하지 않을 수 있다.[1]

```algol68

'''mode''' '''node''' = '''union''' ('''실수'''(real), '''정수'''(int), '''문자열'''(string), '''무효'''(void));

'''node''' n := "abc";

'''case''' n '''in'''

('''실수'''(real) r): print(("실수:", r)),

('''정수'''(int) i): print(("정수:", i)),

('''문자열'''(string) s): print(("문자열:", s)),

('''무효'''(void)): print(("무효:", "비어있음")),

'''out''' print(("?:", n))

'''esac'''

```

C/C++ 공용체 유형의 구문과 캐스팅 개념은 태그가 없는 형태이지만, ALGOL 68에서 파생되었다.[1]

COBOL에서 공용체 데이터 항목은 `RENAMES` (66 레벨) 키워드나 `REDEFINES` 키워드를 사용하여 정의할 수 있다. `RENAMES`는 두 번째 영숫자 데이터 항목을 이전 데이터 항목과 동일한 메모리 위치에 매핑하는 방식이다. `REDEFINES`는 두 번째 항목이 첫 번째 항목 위에서 ''재정의''되어 두 항목이 메모리에서 동일한 주소를 공유하며, 따라서 동일한 기본 데이터 바이트를 공유하는 방식이다.

파스칼에서 공용체를 만드는 방법에는 변형 레코드를 사용하는 표준적인 방법과 `absolute` 키워드를 사용하여 변수를 다른 변수와 같은 메모리 위치에 할당하는 비표준적인 방법이 있다.

PL/I에서 공용체는 "cell"이라고 불렸으며,[5] 여러 컴파일러에서 여전히 공용체의 동의어로 사용된다. 공용체 선언은 구조체 정의와 유사하며, 공용체 선언 내의 동일한 수준의 요소들은 동일한 저장 공간을 차지한다. 공용체의 요소는 구조체와 배열을 포함한 모든 데이터 유형이 될 수 있다.[6]

Rust는 태그 유니온과 비태그 유니온을 모두 구현한다. 태그 유니온은 `enum` 키워드를 사용하여 구현되며, Rust의 열거형 변형은 튜플 또는 구조체의 형태로 추가 데이터를 포함할 수 있어 단순한 열거형이 아닌 태그 유니온을 만든다.[7] 비태그 유니온은 `union` 키워드를 사용하여 지원하며, `#[repr(C)]` 속성이 있는 유니온은 C의 해당 유니온과 정확히 동일하게 메모리에 배치된다.[9]

C#은 공용체 전용 구문은 없지만, `System.Runtime.InteropServices.StructLayoutAttribute`와 `System.Runtime.InteropServices.FieldOffset`를 사용하여 메모리 레이아웃을 명시적으로 지정함으로써 공용체와 유사한 동작을 구현할 수 있다.[16]

PHP 8.0부터 공용체 형식을 지원한다.[11] 값은 암묵적으로 형식으로 "태그"된다.

파이썬 3.5부터 타입 힌트를 지원하며,[12] 파이썬 3.10부터는 `|` 연산자를 사용한 공용체 타입 표현을 지원한다.[13]

TypeScript는 공용체 형식을 지원한다.[14] 값은 언어에 의해 암묵적으로 형식으로 "태그"되며, 기본 값의 경우 `typeof` 호출을 사용하고 복잡한 데이터 형식의 경우 `instanceof` 비교를 사용하여 검색할 수 있다.

3. 1. C/C++

CC++에서 공용체는 `union` 키워드를 사용하여 정의한다. 구조체와 유사한 문법을 가지지만, 각 데이터 멤버가 동일한 메모리 주소를 공유한다는 점이 다르다. 구조체와 마찬가지로 데이터 멤버는 기본값이 아니어도 되며, 실제로 구조체나 다른 공용체일 수도 있다.

C++(C++11 이후)에서는 생성자, 소멸자, 복사 생성자 또는 비자명 복사 할당 연산자를 갖는 모든 유형의 데이터 멤버를 허용한다. 예를 들어, 표준 C++ string을 공용체의 멤버로 가질 수 있다.

C++와 C11, 그리고 많은 컴파일러에서 비표준 확장으로, 공용체는 익명으로 사용될 수 있다. 익명 공용체의 데이터 멤버는 직접 접근할 수 있다. 익명 공용체는 기존 공용체에 비해 몇 가지 제한 사항이 있는데, C11에서는 다른 구조체 또는 공용체의 멤버여야 하고,[2] C++에서는 메서드나 접근 지정자를 가질 수 없다.[3]

C 프로그래밍에서 공용체를 사용하는 일반적인 관용구 중 하나는 C++에서 `reinterpret_cast`라고 부르는 것을 수행하는 것이다. 이는 공용체의 한 필드에 값을 할당하고 다른 필드에서 읽는 방식으로 수행되며, 값의 원시 표현에 의존하는 코드에서 사용된다.

3. 1. 1. C/C++ 공용체 사용의 예

C와 C++에서 공용체는 구조체(struct)와 거의 동일하게 표현되지만, 각 데이터 멤버는 동일한 메모리 주소에 위치한다. 구조체와 마찬가지로 데이터 멤버는 기본 값이 아니어도 되며, 실제로 구조체나 다른 공용체일 수도 있다. C++(C++11 이후)는 완전한 생성자/소멸자 및/또는 복사 생성자 또는 비자명 복사 할당 연산자를 갖는 모든 유형의 데이터 멤버를 허용한다. 예를 들어 표준 C++ string을 공용체의 멤버로 갖는 것이 가능하다.

공용체의 주요 사용법은 서로 다른 데이터 유형으로 공통 위치에 접근할 수 있도록 하는 것이다. 예를 들어 하드웨어 입/출력 접근, 비트 필드 및 워드 공유 또는 타입 펀닝이 있다. 공용체는 또한 낮은 수준의 다형성을 제공할 수 있다. 그러나 타입 검사가 없으므로, 서로 다른 컨텍스트에서 적절한 필드에 접근하는 것은 프로그래머의 책임이다. 공용체 변수의 관련 필드는 일반적으로 다른 변수(아마도 외부 구조체에)의 상태에 의해 결정된다.

일반적인 C 프로그래밍 관용구 중 하나는 공용체를 사용하여 C++에서 `reinterpret_cast`라고 부르는 것을 수행하는 것이다. 이는 공용체의 한 필드에 할당하고 다른 필드에서 읽는 방식으로 수행되며, 값의 원시 표현에 의존하는 코드에서 사용된다. 실용적인 예는 IEEE 표현을 사용하여 제곱근을 계산하는 방법이다. 그러나 이것은 일반적으로 공용체를 안전하게 사용하는 방법은 아니다.

C와 C++에서 구문은 다음과 같다.

```c

union <이름>

{

<자료형> <첫 번째 변수 이름>;

<자료형> <두 번째 변수 이름>;

.

.

.

<자료형> ;

} <공용체 변수 이름>;

```

구조체는 다음 예제와 같이 공용체의 멤버가 될 수도 있다.

```c

union 이름1

{

struct 이름2

{

int a;

float b;

char c;

} svar;

int d;

} uvar;

```

이 예제는 변수 `uvar`를 공용체(`name1`으로 태그됨)로 정의하며, 여기에는 두 개의 멤버가 포함되어 있다. 즉, 구조체(`name2`로 태그됨) `svar` (차례로 세 개의 멤버를 포함)와 정수 변수 `d`가 있다.

공용체는 구조체와 배열 내에서 발생할 수 있으며 그 반대도 가능하다.

```c

struct

{

int flags;

char *name;

int utype;

union {

int ival;

float fval;

char *sval;

} u;

} symtab[NSYM];

```

`ival`의 수는 `symtab[i].u.ival`로 참조되고, 문자열 `sval`의 첫 번째 문자는 `*symtab[i].u.sval` 또는 `symtab[i].u.sval[0]`으로 참조된다.

3. 1. 2. C 언어

C 언어는 태그 없는 공용체를 지원한다. C의 공용체는 모든 멤버의 오프셋이 0인 (즉, 첫 번째 바이트부터 시작하는) 구조체이며, 선언에 예약어 `struct` 대신 공용체를 의미하는 `union`을 사용하는 것을 제외하고 구조체와 완전히 동일한 구문으로 선언 및 정의된다.[16] 멤버에 대한 접근도 구조체와 마찬가지로 `.` 연산자 또는 `->` 연산자로 수행할 수 있다. 공용체 전체의 크기는 최소한 멤버 중에서 가장 큰 것을 저장할 수 있는 크기로 결정된다(뒤에 패딩될 수 있음).[16]

공용체의 초기화는 처음에 선언된 멤버의 유형으로 수행해야 한다.[16]

구조체를 포함하는 공용체에 대해서는 배치가 특별히 규정되어 있으며, 공용체의 멤버로서, 첫 번째 멤버의 유형이 동일한 구조체를 여러 개 가지고 있는 경우, 해당 구조체의 첫 번째 멤버에 한하여 정확히 겹치는 것이 보장된다. 즉, 첫 번째 멤버는 다른 구조체의 첫 번째 멤버의 이름으로도 올바르게 참조할 수 있다. 이 사양은 유사한 구성의 구조체를 모아 놓은 공용체에서 첫 번째 멤버를 태그 정보로 사용하기 위해 유용하다.[16]

C 코드 예시는 다음과 같다.

```c

#include

#include

/* 공용체 MyUnion1을 정의 */

union MyUnion1 {

double x;

int y;

char z[10];

/* double, int, char[10] 중 하나를 저장할 수 있다 */

};

/* 공용체 MyUnion2를 정의: 구조체의 공용체 */

union MyUnion2 {

struct Foo {

int ifoo;

double dfoo;

} foo; /* 구조체 Foo와 멤버 foo의 정의 */

struct Bar {

int ibar;

void *pbar;

} bar; /* 구조체 Bar와 멤버 bar의 정의 */

}; /* Foo와 Bar의 첫 번째 멤버는 동일한 유형(int) */

/* 공용체를 사용해 보기 */

int main(void) {

/* 공용체 변수의 선언 및 초기화 */

/* 첫 번째 멤버의 유형(double)으로 초기화해야 한다 */

union MyUnion1 u1 = { 100.0 };

union MyUnion2 u2;

#ifdef __cplusplus

/* C++에서는 중첩된 유형은 범위 지정 연산자로 이름을 한정해야 한다 */

MyUnion2::Foo f = { 3, 0.14 };

#else

struct Foo f = { 3, 0.14 };

#endif

u1.y = 42; /* int형 값을 쓴다 */

strcpy(u1.z, "UnionTest"); /* char 배열을 쓴다 */

/* u1.y += 100; */ /* 부적절: char 배열이 들어있는 u1을 int형으로 취급한다 */

u2.foo = f; /* Foo 구조체를 쓴다 */

u2.bar.ibar = 9; /* OK: Bar의 첫 번째 멤버로 액세스해도 좋다 */

/* u2.bar.pbar = NULL; */ /* 이건 안 돼: 지금 u2에 들어있는 건 어디까지나 Foo형 */

return 0;

}

```

C99까지는 익명의 구조체 및 공용체가 허용되지 않았지만, C11에서는 허용되게 되었다.[16][17]

C11에서 허용되는 익명 공용체 사용 예시는 다음과 같다.

```c

#include

enum VariantType {

VariantTypePointer,

VariantTypeInt,

VariantTypeDouble,

};

struct Variant {

enum VariantType type;

union { /* 익명의 공용체 */

void* p;

int i;

double d;

};

};

int main(void) {

struct Variant v1;

v1.type = VariantTypePointer;

v1.p = NULL;

v1.type = VariantTypeInt;

v1.i = 999;

v1.type = VariantTypeDouble;

v1.d = 0.5;

return 0;

}

3. 1. 3. C++

C++에서 공용체는 클래스(및 구조체)의 일종으로, C의 공용체 기능에 멤버 함수를 가질 수 있는 등의 기능이 추가되었다. 그러나 일반적인 클래스(및 구조체)에 비해 다음과 같은 제약이 있었다.[18]

  • 상속 기능이 없다.
  • 기저 클래스를 가질 수 없다.
  • 공용체로부터 파생될 수 없다.
  • 가상 함수를 가질 수 없다.
  • 정적 멤버를 가질 수 없다.
  • POD가 아닌 클래스의 객체형 멤버를 가질 수 없다.
  • 참조형 멤버를 가질 수 없다.


이러한 제약의 일부는 C++11에서 완화되었다.

C++에서는 공용체 태그명[19]을 생략하여 정의함으로써 스코프를 형성하지 않는 무명 공용체를 만들 수 있다. 하지만 무명 구조체는 허용되지 않는다.[20]

```cpp

// 무명 공용체

union {

int i;

char c;

};

i = 777; // 멤버는 외부에서 보임

c = 'X';

```

C++에서 `reinterpret_cast`는 공용체를 이용하여 수행할 수 있다. 이는 공용체의 한 필드에 할당하고 다른 필드에서 읽는 방식으로 수행되며, 값의 원시 표현에 의존하는 코드에서 사용된다. 실용적인 예는 IEEE 표현을 사용하여 제곱근을 계산하는 방법이다. 그러나 이것은 일반적으로 공용체를 안전하게 사용하는 방법은 아니다.

```cpp

#include

#include

int main() {

union {

float f;

uint32_t d; // float가 32비트라고 가정

};

f = 3.14f;

std::cout << "3.14f의 16진수 표현:"

<< std::hex << d << '\n';

return 0;

}

```

표준 라이브러리의 것을 포함하여 대부분의 클래스는 공용체에 저장할 수 없다. 이는 공용체가 태그 정보를 갖지 않기 때문에 생성자소멸자를 올바르게 실행하는 코드를 자동 생성할 수 없기 때문이다. 따라서 C++에서 공용체가 적극적으로 활용되는 경우는 적다.

공용체와 유사한 C++ 기능으로 `reinterpret_cast`가 있으며, 바이트열 재해석 용도로는 이것이 일반적으로 사용된다.

3. 2. ALGOL 68

ALGOL 68은 태그가 붙은 공용체를 지원하며, 런타임에 구성 요소 유형을 구별하고 추출하기 위해 case 절을 사용한다. 다른 공용체를 포함하는 공용체는 모든 구성 요소 가능성의 집합으로 취급되며, 문맥상 필요할 경우 공용체는 자동으로 더 넓은 공용체로 변환된다. 공용체는 런타임에 구별할 수 있는 값을 명시적으로 포함하지 않을 수 있다. 다음은 그 예시이다.[1]

'''mode''' '''node''' = '''union''' ('''실수'''(real), '''정수'''(int), '''문자열'''(string), '''무효'''(void));

'''node''' n := "abc";

'''case''' n '''in'''

('''실수'''(real) r): print(("실수:", r)),

('''정수'''(int) i): print(("정수:", i)),

('''문자열'''(string) s): print(("문자열:", s)),

('''무효'''(void)): print(("무효:", "비어있음")),

'''out''' print(("?:", n))

'''esac'''

C/C++ 공용체 유형의 구문과 캐스팅 개념은 태그가 없는 형태이지만, ALGOL 68에서 파생되었다.[1]

3. 3. COBOL

COBOL에서 공용체 데이터 항목은 두 가지 방법으로 정의할 수 있다. 첫 번째 방법은 `RENAMES` (66 레벨) 키워드를 사용하는 것이다. 이는 실제로 두 번째 영숫자 데이터 항목을 이전 데이터 항목과 동일한 메모리 위치에 매핑한다. 아래 예제 코드에서 `PERSON-REC` 데이터 항목은 다른 그룹과 숫자 데이터 항목을 포함하는 그룹으로 정의된다. `PERSON-DATA`는 `PERSON-REC`의 이름을 변경하는 영숫자 데이터 항목으로 정의되며, 그 안에 포함된 데이터 바이트를 문자 데이터로 취급한다.

```cobol

01 PERSON-REC.

05 PERSON-NAME.

10 PERSON-NAME-LAST PIC X(12).

10 PERSON-NAME-FIRST PIC X(16).

10 PERSON-NAME-MID PIC X.

05 PERSON-ID PIC 9(9) PACKED-DECIMAL.

01 PERSON-DATA RENAMES PERSON-REC.

```

두 번째 공용체 유형 정의 방법은 `REDEFINES` 키워드를 사용하는 것이다. 아래 예제 코드에서 `VERS-NUM` 데이터 항목은 버전 번호를 포함하는 2바이트 이진 정수로 정의된다. 두 번째 데이터 항목 `VERS-BYTES`는 두 문자 영숫자 변수로 정의된다. 두 번째 항목이 첫 번째 항목 위에서 ''재정의''되므로 두 항목은 메모리에서 동일한 주소를 공유하며, 따라서 동일한 기본 데이터 바이트를 공유한다. 첫 번째 항목은 두 데이터 바이트를 이진 값으로 해석하는 반면, 두 번째 항목은 바이트를 문자 값으로 해석한다.

```cobol

01 VERS-INFO.

05 VERS-NUM PIC S9(4) COMP.

05 VERS-BYTES PIC X(2)

REDEFINES VERS-NUM.

3. 4. Pascal

파스칼에서 공용체를 만드는 방법에는 두 가지가 있다. 하나는 변형 레코드를 사용하는 표준적인 방법이고, 다른 하나는 `absolute` 키워드를 사용하여 변수를 다른 변수와 같은 메모리 위치에 할당하는 비표준적인 방법이다. 모든 파스칼 컴파일러가 변형 레코드를 지원하지만, 일부만 `absolute` 변수를 지원한다.

예시를 위해, 다음은 모두 정수형이라고 가정한다. '''바이트'''는 8비트, '''워드'''는 16비트, '''정수'''는 32비트이다.

다음은 비표준 `absolute` 형태를 보여주는 예시이다.

```pascal

var

A: Integer;

B: array[1..4] of Byte absolute A;

C: Integer absolute 0;

```

첫 번째 예시에서 배열 B의 각 요소는 변수 A의 특정 바이트에 매핑된다. 두 번째 예시에서 변수 C는 정확한 기계 주소 0에 할당된다.

다음 예시에서 레코드는 변형을 가지며, 그 중 일부는 다른 변형과 같은 위치를 공유한다.

```pascal

type

Shape = (Circle, Square, Triangle);

Dimensions = record

case Figure: Shape of

Circle: (Diameter: real);

Square: (Width: real);

Triangle: (Side: real; Angle1, Angle2: 0..360)

end;

3. 5. PL/I

PL/I에서 공용체는 "cell"이라고 불렸으며,[5] 여러 컴파일러에서 여전히 공용체의 동의어로 사용된다. 공용체 선언은 구조체 정의와 유사하며, 공용체 선언 내의 동일한 수준의 요소들은 동일한 저장 공간을 차지한다. 공용체의 요소는 구조체와 배열을 포함한 모든 데이터 유형이 될 수 있다.[6] 다음은 vers_num과 vers_bytes가 동일한 저장 공간을 차지하는 예시이다.

```cobolfree

1 vers_info union,

5 vers_num fixed binary,

5 vers_bytes pic '(2)A';

```

공용체 선언의 대안은 DEFINED 속성인데, 이는 저장 공간의 대체 선언을 허용하지만, 기본 및 정의된 변수의 데이터 유형은 일치해야 한다.[6]

3. 6. Rust

Rust는 태그 유니온과 비태그 유니온을 모두 구현한다. Rust에서 태그 유니온은 `enum` 키워드를 사용하여 구현된다. 대부분의 다른 언어의 열거형과는 달리, Rust의 열거형 변형은 튜플 또는 구조체의 형태로 추가 데이터를 포함할 수 있어 단순한 열거형이 아닌 태그 유니온을 만든다.[7]

Rust는 `union` 키워드를 사용하여 비태그 유니온도 지원한다. Rust에서 유니온의 메모리 레이아웃은 기본적으로 정의되지 않지만,[8] `#[repr(C)]` 속성이 있는 유니온은 C의 해당 유니온과 정확히 동일하게 메모리에 배치된다.[9] 유니온의 필드를 읽는 것은 `unsafe` 함수 또는 블록 내에서만 수행할 수 있는데, 이는 컴파일러가 유니온의 데이터가 필드 유형에 유효한지 보장할 수 없기 때문이다. 그렇지 않은 경우 정의되지 않은 동작이 발생한다.[10]

태그된 공용체는 `enum` 키워드를 사용하며, 튜플과 구조체 변형을 포함할 수 있다. 태그되지 않은 공용체는 `union` 키워드를 사용한다. 태그되지 않은 공용체의 필드에서 값을 읽는 것은 공용체의 데이터가 해당 필드의 유형으로 유효하지 않은 경우 정의되지 않은 동작을 발생시키므로 `unsafe` 블록이 필요하다.

3. 7. C#

C#은 공용체 전용 구문은 없지만, `System.Runtime.InteropServices.StructLayoutAttribute`와 `System.Runtime.InteropServices.FieldOffset`를 사용하여 메모리 레이아웃을 명시적으로 지정함으로써 공용체와 유사한 동작을 구현할 수 있다.[16]

다음은 C#에서 공용체와 유사한 동작을 구현하는 예시이다.

```csharp

using System;

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Explicit)]

public struct Union {

// 메모리 레이아웃의 오프셋을 지정한다.

[FieldOffset(0)]

public byte B0;

[FieldOffset(1)]

public byte B1;

[FieldOffset(2)]

public byte B2;

[FieldOffset(3)]

public byte B3;

// 메모리 레이아웃의 오프셋을 B0, B1, B2, B3와 같은 영역에 겹쳐둔다.

[FieldOffset(0)]

public int N;

}

class Program {

static void Main() {

var u = new Union();

// N 영역에 값을 쓴다.

u.N = 0x12345678;

// B0, B1, B2, B3 영역에서 값을 읽는다.

Console.WriteLine(u.N.ToString("X")); // ⇒ 12345678

Console.WriteLine(u.B0.ToString("X")); // ⇒ 리틀 엔디안에서는 78

Console.WriteLine(u.B1.ToString("X")); // ⇒ 리틀 엔디안에서는 56

Console.WriteLine(u.B2.ToString("X")); // ⇒ 리틀 엔디안에서는 34

Console.WriteLine(u.B3.ToString("X")); // ⇒ 리틀 엔디안에서는 12

}

}

3. 8. PHP

PHP 8.0부터 공용체 형식을 지원한다.[11] 값은 암묵적으로 형식으로 "태그"되며, `gettype()` 함수를 통해 검색할 수 있다.

3. 9. Python

파이썬 3.5부터 타입 힌트를 지원하며,[12] 파이썬 3.10부터는 `|` 연산자를 사용한 공용체 타입 표현을 지원한다.[13]

3. 10. TypeScript

TypeScript는 공용체 형식을 지원한다.[14] 값은 언어에 의해 암묵적으로 형식으로 "태그"되며, 기본 값의 경우 `typeof` 호출을 사용하고 복잡한 데이터 형식의 경우 `instanceof` 비교를 사용하여 검색할 수 있다. 사용법이 겹치는 형식(예: 문자열과 배열 모두에 slice 메서드가 있고, 더하기 연산자가 문자열과 숫자 모두에서 작동함)은 이러한 기능을 사용하기 위해 추가적인 축소를 필요로 하지 않는다.

```typescript

function successor(n: number | bigint): number | bigint {

// 동일한 연산을 지원하는 형식은 축소할 필요가 없습니다.

return ++n;

}

function dependsOnParameter(v: string | Array | number) {

// 개별 형식은 축소가 필요합니다.

if (v instanceof Array) {

// 무언가 수행

} else if (typeof(v) === "string") {

// 다른 무언가 수행

} else {

// 숫자여야 함

}

}

4. 한국 소프트웨어 산업에서의 공용체 활용

CC++에서 공용체는 메모리 사용을 최적화하고 하드웨어와 직접 상호작용하는 데 유용하므로, 자원 제약이 있는 환경이나 성능이 중요한 시스템에서 활용된다. 특히, 임베디드 시스템 개발에서 장치의 메모리 사용량을 줄이고, 하드웨어 레지스터에 직접 접근하는 데 사용된다.

C++에서 공용체는 클래스(구조체 포함)의 일종으로, C의 공용체에 멤버 함수를 추가하는 등 기능을 더했다. 다만, 일반적인 클래스(구조체 포함)에 비해 다음과 같은 제약이 있다.


  • 상속 기능이 없다.
  • 기저 클래스가 될 수 없다.
  • 공용체로부터 파생될 수 없다.
  • 가상 함수를 가질 수 없다.
  • 정적 멤버를 가질 수 없다.
  • POD가 아닌 클래스의 객체형 멤버를 가질 수 없다.[18]
  • 참조형 멤버를 가질 수 없다.


C++11에서 일부 제약은 완화되었다.

표준 라이브러리의 클래스를 포함한 대부분의 클래스는 공용체에 저장할 수 없다. 공용체는 태그 정보가 없어서 생성자소멸자를 올바르게 실행하는 코드를 자동 생성할 수 없기 때문이다. 따라서 C++에서 공용체가 적극적으로 활용되는 경우는 적다.

공용체와 유사한 C++ 기능으로 `reinterpret_cast`가 있으며, 바이트열 재해석에는 이것이 일반적으로 사용된다.

```cpp

#include

#include

// 공용체 U를 정의

union U {

private:

double x;

int y;

//std::string z; // 부정: POD가 아닌 클래스는 멤버로 할 수 없음

std::string *z; // 포인터는 가능

public:

// 생성자, 소멸자 및 멤버 함수를 가질 수 있음

explicit U(double d) : x(d) { std::cout << "U(double)" << std::endl; }

explicit U(int i) : y(i) { std::cout << "U(int)" << std::endl; }

~U() { std::cout << "~U()" << std::endl; }

double getDouble() const { return x; }

int getInt() const { return y; }

};

int main(void) {

U u1(3.14), u2(22); // 생성자에 의한 공용체의 구축

std::cout << u1.getDouble() << std::endl; // 멤버 함수 호출

std::cout << u2.getInt() << std::endl;

//std::cout << u2.getDouble() << std::endl; // ※무슨 일이 일어날지 알 수 없음

// 무명 공용체

union {

int i;

char c;

};

i = 777; // 멤버는 외부에서 보임

c = 'X';

std::cout << c << std::endl;

//std::cout << i << std::endl; // ※무슨 일이 일어날지 알 수 없음

return 0;

}

4. 1. 임베디드 시스템

공용체는 제한된 메모리 환경에서 여러 종류의 데이터를 효율적으로 저장하고 처리해야 할 때 유용하다. 임베디드 시스템에서 하드웨어 레지스터에 직접 접근하여 장치를 제어하는 데 사용될 수 있는데, 이때 공용체를 사용하면 서로 다른 데이터 유형으로 공통 위치에 접근할 수 있다.

4. 2. 시스템 프로그래밍

CC++에서 공용체는 운영체제 커널, 디바이스 드라이버 등에서 메모리 관리 및 하드웨어 상호작용을 위해 사용된다.[18] 예를 들어 하드웨어 입/출력 접근, 비트 필드 및 워드 공유, 타입 펀닝 등에 사용된다.[19] 공용체는 낮은 수준의 다형성을 제공할 수 있지만, 타입 검사가 없으므로 프로그래머가 적절한 필드에 접근해야 한다.[20]

일반적인 C 프로그래밍 관용구 중 하나는 공용체를 사용하여 C++에서 `reinterpret_cast`라고 부르는 것을 수행하는 것이다. 이는 한 필드에 값을 할당하고 다른 필드에서 읽는 방식으로 수행되며, 값의 원시 표현에 의존하는 코드에서 사용된다. 실용적인 예로는 IEEE 표현을 사용하여 제곱근을 계산하는 방법이 있다.

4. 3. 네트워크 프로그래밍

CC++에서 공용체는 구조체와 유사하게 표현되지만, 모든 데이터 멤버가 동일한 메모리 주소를 공유한다. 이러한 특성은 네트워크 프로토콜 헤더와 같이 다양한 형식의 데이터를 처리해야 할 때 유용하다. 공용체를 사용하면 서로 다른 데이터 유형으로 공통 메모리 위치에 접근할 수 있다.[18]

C 프로그래밍에서는 공용체를 사용하여 C++의 `reinterpret_cast`와 비슷한 기능을 수행하는 경우가 있다. 이는 한 필드에 값을 할당하고 다른 필드에서 읽는 방식으로, 값의 원시 표현에 의존하는 코드에서 사용된다. 예를 들어, IEEE 표현을 사용하여 제곱근을 계산하는 방법이 있다. 하지만 이는 일반적으로 안전한 방법은 아니다.[19]

C++에서 공용체는 멤버 함수를 가질 수 있다는 점 등 C의 공용체에 비해 기능이 추가되었지만, 상속 기능이 없고, 정적 멤버나 참조형 멤버를 가질 수 없는 등의 제약이 있다. 이러한 제약 때문에 표준 라이브러리의 클래스를 포함한 대부분의 클래스는 공용체에 저장할 수 없다. C++에서는 바이트열 재해석을 위해 `reinterpret_cast`를 사용하는 것이 일반적이다.[20]

5. 결론

CC++에서 공용체는 여러 데이터 멤버가 동일한 메모리 주소를 공유하는 자료형이다. 이는 서로 다른 데이터 유형으로 공통 위치에 접근하거나, 타입 펀닝을 수행하거나, 낮은 수준의 다형성을 구현하는 데 사용될 수 있다. 하지만 타입 검사가 없으므로 프로그래머는 올바른 필드 접근에 대한 책임을 져야 한다.[18]

C 프로그래밍에서는 공용체를 사용하여 C++의 `reinterpret_cast`와 유사한 작업을 수행하는 것이 일반적이다. 이는 한 필드에 값을 할당하고 다른 필드에서 읽는 방식으로, 값의 원시 표현에 의존하는 코드에서 사용된다. 예를 들어, IEEE 표현을 사용하여 제곱근을 계산하는 방법이 있다. 그러나 이는 일반적으로 공용체를 안전하게 사용하는 방법은 아니다.

C++에서 공용체는 클래스의 일종으로, 멤버 함수를 가질 수 있지만 상속, 정적 멤버, POD가 아닌 클래스 객체형 멤버, 참조형 멤버를 가질 수 없다는 제약이 있다. 이러한 제약 중 일부는 C++11에서 완화되었다.[19] C++에서는 무명 공용체를 정의하여 스코프를 형성하지 않고 멤버에 직접 접근할 수 있다.[20]

C++에서 공용체가 널리 사용되지 않는 이유는 태그 정보가 없어 생성자소멸자를 자동으로 생성할 수 없기 때문이다. 바이트열 재해석에는 일반적으로 `reinterpret_cast`가 사용된다.

아래는 C++에서 공용체를 사용하는 예시 코드이다.

```cpp

#include

#include

// 공용체 U 정의

union U {

private:

double x;

int y;

//std::string z; // 부정: POD가 아닌 클래스는 멤버로 할 수 없음

std::string *z; // 포인터는 가능

public:

// 생성자, 소멸자 및 멤버 함수를 가질 수 있음

explicit U(double d) : x(d) { std::cout << "U(double)" << std::endl; }

explicit U(int i) : y(i) { std::cout << "U(int)" << std::endl; }

~U() { std::cout << "~U()" << std::endl; }

double getDouble() const { return x; }

int getInt() const { return y; }

};

int main(void) {

U u1(3.14), u2(22); // 생성자에 의한 공용체의 구축

std::cout << u1.getDouble() << std::endl; // 멤버 함수 호출

std::cout << u2.getInt() << std::endl;

//std::cout << u2.getDouble() << std::endl; // ※무슨 일이 일어날지 알 수 없음

// 무명 공용체

union {

int i;

char c;

};

i = 777; // 멤버는 외부에서 보임

c = 'X';

std::cout << c << std::endl;

//std::cout << i << std::endl; // ※무슨 일이 일어날지 알 수 없음

return 0;

}

참조

[1] 논문 The Development of the C Language http://www.bell-labs[...] 1993-03
[2] 웹사이트 6.63 Unnamed Structure and Union Fields https://gcc.gnu.org/[...] 2016-12-29
[3] 웹사이트 CUnionsForNamespaces https://utcc.utoront[...]
[4] 웹사이트 Common Type Attributes: transparent_union https://gcc.gnu.org/[...]
[5] 서적 IBM System/360 PL/I Language Specifications http://bitsavers.org[...] 2018-01-22
[6] 서적 Enterprise PL/I for z/OS PL/I for AIX IBM Developer for z Systems PL/I for windows Language Reference http://publibz.bould[...] 2018-01-22
[7] 웹사이트 How Rust Implements Tagged Unions - Pat Shaughnessy https://patshaughnes[...] 2023-04-25
[8] 웹사이트 Union types - The Rust Reference https://doc.rust-lan[...] 2023-04-25
[9] 웹사이트 Type layout - The Rust Reference https://doc.rust-lan[...] 2023-04-25
[10] 웹사이트 Unions - The Rust Reference https://doc.rust-lan[...] 2023-04-25
[11] 웹사이트 PHP 8.0: Union Types https://php.watch/ve[...] 2020-11-30
[12] 웹사이트 typing — Support for type hints — Python 3.9.7 documentation https://docs.python.[...] 2021-09-08
[13] 웹사이트 PEP 604 -- Allow writing union types as X {{!}} Y https://www.python.o[...] 2021-09-08
[14] 웹사이트 Handbook - Unions and Intersection Types https://www.typescri[...] 2020-11-30
[15] 문서 Microsoft Windows SDK의 에서는, 공용체를 이용한VARIANT형이 정의되어 있다.
[16] 웹사이트 공용체선언 - cppreference.com https://ja.cpprefere[...]
[17] 문서 많은 C컴파일러에서는 (C11보다 앞선 시대부터) 확장으로서 무명의 구조체 및 공용체를 서포트하고 있다.
[18] 문서 모든 공용체 맴버는 반드시 POD이지만, 공용체 자신이 POD가 된다고는 할 수 없다. 공용체는 유저 정의의 컨스트럭터등을 가질 수 있기 때문이다.
[19] 문서 공용체의 형명을 정하는 식별자(union xxx {...};xxx)로, 서두의 설명에 있는「태그 정보」와는 별개.
[20] 문서 많은 C++컴파일러에서는 확장으로서 무명의 구조체를 서포트하고 있다.



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com