packed union — 메모리 배치가 정해진 공용체
packed union — 메모리 배치가 정해진 공용체
보통의 union은 메모리상에서 어떤 모습으로 놓일지 마음대로입니다. 컴파일러가 가장 큰 필드 크기에 맞춰 넉넉히 잡아주죠. 그런데 어떤 상황에서는 union이 정확히 어떤 비트 배치로 저장되는지가 중요할 때가 있어요. 그럴 때 쓰는 게 바로 packed union입니다.
본문
packed union은 메모리 안에서의 배치가 명확하게 정의된 union입니다. 그래서 packed struct 안에 들어갈 자격을 갖추게 되죠.
const U = packed union {
a: u4,
b: i4,
};
여기서 짚고 넘어갈 규칙이 하나 있어요. packed union의 모든 필드는 똑같은 @bitSizeOf를 가져야 합니다. 위 예에서 a: u4와 b: i4는 둘 다 4비트라서 문제없이 통과해요. 필드마다 비트 크기가 달라지면 배치를 정할 수 없으니 이 조건을 꼭 지켜야 하죠.
그리고 packed union을 서로 비교하면, 실제로는 그 뒤에 깔려 있는 정수(backing integer)를 비교하게 됩니다. 이 비교가 동작하는 연산자는 ==와 !=뿐이에요. 예시를 하나 볼게요.
const std = @import("std");
const expectEqual = std.testing.expectEqual;
test "packed union equality" {
const U = packed union {
a: u4,
b: i4,
};
const x: U = .{ .a = 3 };
const y: U = .{ .b = 3 };
try expectEqual(x, y);
}
$ zig test test_packed_union_equality.zig
1/1 test_packed_union_equality.test.packed union equality...OK
All 1 tests passed.
흥미로운 점이 보이시죠? x는 .a = 3으로, y는 .b = 3으로 다른 필드를 통해 값을 넣었는데도 둘은 같다고 판정됩니다. 필드 구성이 다르더라도 뒤에 깔린 정수 값이 같으니까요. 이렇게 packed union은 필드가 아니라 비트 배치(backing integer)로 비교된다는 게 핵심입니다.
더 알아보기
packed union의 뒷정수(backing integer)를 직접 다루고 싶다면 아래 내장 함수를 확인해 봐요.
@backingInt— packed union 값을 그 뒷정수로 바꿔줍니다.@fromBackingInt— 뒷정수 값을 packed union 값으로 만들어줍니다.