API를 사용하다 보면 공용체를 사용해야할 순간이 오기 마련입니다.

API 사용이 아니더라도, 값을 쪼갠다던지 아니면 플래그를 사용한다던지 할 때에도 필요해집니다.

이럴 땐 어떻게 해야할까요?


C/C++의 경우 union 이란 키워드가 친절하게 그 작업을 모두 해주었지만, 

C#은 물론 VB에도 union 키워드는 찾아볼 수가 없습니다.


하지만! union 키워드가 없음에도 불구하고 공용체를 사용하는 방법이 있습니다.

바로, System.Runtime.InteropServices 네임스페이스에 정의되어 있는 FieldOffset, StructLayout 특성을 사용하는 것입니다.


FieldOffset 특성의 경우, 구조체 또는 클래스의 멤버가 몇 번째 주소에 위치할 것인지를 설정해주고,

StructLayout 특성의 경우, 구조체 또는 클래스의 형식, 크기, 자료형 크기 맞춤(Pack) 및 문자셋에 대한 정보를 설정해 줍니다.


Tip!

특성 클래스의 이름이 Attribute 로 끝나는 경우, Attribute 는 생략이 가능합니다.

예: 특성 클래스의 이름이 NameAttribute 인 경우, Name 으로 사용이 가능합니다. (끝의 Attribute 가 생략됨)


그리고, 대부분의 특성은 생성자가 모든 것을 결정하기에 속성 및 메서드는 신경쓰지 않으셔도 무관합니다.




FieldOffset 특성 클래스부터 살펴보도록 하겠습니다.


생성자:

C#


VB.NET


총 한 개의 매개 변수(offset)를 필요로 하고 있습니다.

이 매개 변수가 필드가 몇 번째 주소에 위치할 것인지를 결정해줍니다!




그럼 이제, StructLayout 특성 클래스를 살펴보도록 하겠습니다.


생성자:

C#


VB.NET



매개 변수 설명은 아래와 같습니다.

layoutKind - 형식을 설정합니다. (멤버를 배치할 때 순차적으로 할지, 명시적으로 할지, 자동으로 할지를 결정)




보는 것보단 역시 직접 해보는게 이해가 빠를거라 생각됩니다.

일단 아래의 코드를 보시지요~!!


C#



VB.NET


주의할 점!

FieldOffset 특성을 이용하여 오프셋을 설정하는 경우에는

반드시 !!!! StrucLayout 특성에 LayoutKind.Explicit 값을 사용해야 합니다!



위의 구조체엔 총 3개의 멤버가 있습니다.

Base 는 부호 있는 4바이트 정수 (INT)

Low, High 는 부호 있는 2바이트 정수 (SHORT)

로 선언되어 있습니다.


그리고 각 멤버의 위치는 어떨까요?

Base 와 Low 멤버는 0, High 멤버는 2 에 위치해 있습니다.

이걸 그림으로 표현하면 이렇게 표현할 수 있습니다.


즉, Base 는 0부터 3까지의 자리를 차지하게 되고 Low 는 0부터 1, High 는 2부터 3까지 자리를 차지하게 됩니다.

그럼 과연 값이 생각한 대로 Base 에는 int 가 들어가고

Low 에는 Base 의 하위 값, High 에는 Base 의 상위 값이 들어가는지 확인을 해보겠습니다.


65535 (unsigned short 의 최대 값) 이하로 값이 입력된 경우,

High 에는 0 이 나타나는 것을 볼 수 있으며, 또한 Base 와 Low 멤버의 값은 동일하게 나타납니다.



65536 (unsigned short 의 최대 값 + 1) 이상으로 값이 입력된 경우,

High 에는 상위 값이 들어간 것을 볼 수 있으며, Low 에는 나머지 값이 들어간 것을 보실 수 있습니다.

그리고 Base 값은 입력한 값 그대로 들어간 것도 보이는군요.




그럼 과연 저 값은 믿을만한가? 한번 계산을 해보죠.

65535를 넘게되면 High 에 값이 1 추가되고 Low 는 0으로 됩니다.

즉 Base 의 값이 65536 이 되었다면, Low 값은 0, High 값은 1이 됩니다.

이 사실에 근거해서....

High 의 값에다 65536을 곱합니다.

144 * 65536 = 9437184


곱한 값의 결과에 나머지 Low 값을 더해줍니다.

9437184 + 22212 = 9459396



이로써 C/C++ 의 공용체를 구현할 수 있다는 것을 입증하였습니다.

그러면 이제 C/C++ 의 #pragma pack() 그리고, 구조체의 크기를 설정하는 것을 구현할 차례입니다.


이건 정말 정말 간단합니다.

StructLayout 특성을 적용할 때 Pack = 값 이렇게 설정만 해주면 됩니다.

크기를 설정하는 것 또한 Size = 값 으로 설정해주시면 됩니다.

코드는 아래와 같습니다.

C#


VB.NET



실행 결과는 이렇습니다.



자료형의 크기를 맞춰준 경우

int, short, byte 각각 4, 2, 1 바이트에 맞춰서 구조체의 크기가 7로 나온반면

크기를 맞춰주지 않은 경우엔

int, short, byte 각각 4, 2, 1 바이트 총 합이 7바이트임에도 불구하고 8로 나오게 되었습니다.


크기를 맞춰주지 않을 경우엔

구조체에 자료형이 byte인 멤버 하나만 있어도 4바이트 혹은 8바이트로 맞춰지게 됩니다. (운영체제 아키텍쳐에 따라 다름)

더 자세한 내용은 위키피디아를 참고하세요..


http://en.wikipedia.org/wiki/Data_structure_alignment



지금까지 C#/VB.NET 에서 공용체를 구현하는 방법에 대해서 설명하였습니다.

궁금한 점, 틀린 것 또는 기타 지적 사항이 있는 경우 댓글로 남겨주세요!!

+ Recent posts