잘못된 포인터 정렬
잘못된 포인터 정렬 (Incorrect Pointer Alignment)
@alignCast를 쓰면 정렬 안전 검사가 들어간다고 했죠. 그 검사가 언제 발동하고 정확히 무슨 일을 하는지가 이번 섹션의 핵심이에요. 정렬이 어긋난 포인터를 만들려고 하면, 시점에 따라 두 가지 방식으로 잡혀요.
본문
먼저 컴파일 타임(compile-time) 에 걸리는 경우예요. @ptrFromInt로 주소 0x1을 가리키는 *align(1) i32 포인터를 만들고, 이걸 @alignCast로 *align(4) i32로 바꿔 볼게요.
comptime {
const ptr: *align(1) i32 = @ptrFromInt(0x1);
const aligned: *align(4) i32 = @alignCast(ptr);
_ = aligned;
}
$ zig test test_comptime_incorrect_pointer_alignment.zig
/home/ci/work/zig-bootstrap/zig/doc/langref/test_comptime_incorrect_pointer_alignment.zig:3:47: error: pointer address 0x1 is not aligned to 4 bytes
const aligned: *align(4) i32 = @alignCast(ptr);
^~~
주소 0x1은 4로 나누어떨어지지 않으니(1 % 4 = 1) 컴파일조차 되지 않아요. 같은 실수를 런타임(runtime) 에 저지르면 이번엔 안전 검사가 패닉을 띄워요. mem.bytesAsSlice로 정렬이 어긋난 위치에서 u32 슬라이스를 만드는 예시를 볼게요.
const mem = @import("std").mem;
pub fn main() !void {
var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
const bytes = mem.sliceAsBytes(array[0..]);
if (foo(bytes) != 0x11111111) return error.Wrong;
}
fn foo(bytes: []u8) u32 {
const slice4 = bytes[1..5];
const int_slice = mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
return int_slice[0];
}
$ zig build-exe runtime_incorrect_pointer_alignment.zig
$ ./runtime_incorrect_pointer_alignment
thread 975613 panic: incorrect alignment
/home/ci/work/zig-bootstrap/zig/doc/langref/runtime_incorrect_pointer_alignment.zig:9:64: 0x11e8c66 in foo (runtime_incorrect_pointer_alignment.zig)
const int_slice = mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
^
/home/ci/work/zig-bootstrap/zig/doc/langref/runtime_incorrect_pointer_alignment.zig:5:12: 0x11e763e in main (runtime_incorrect_pointer_alignment.zig)
if (foo(bytes) != 0x11111111) return error.Wrong;
^
/home/ci/work/zig-bootstrap/out/host/lib/zig/std/start.zig:788:64: 0x11e7c66 in callMain (std.zig)
if (fn_info.param_types.len == 0) return wrapMain(root.main());
^
/home/ci/work/zig-bootstrap/out/host/lib/zig/std/start.zig:248:5: 0x11e75e1 in _start (std.zig)
asm volatile (switch (native_arch) {
^
(process terminated by signal)
bytes[1..5]는 배열을 1바이트 옮긴 위치라서 4바이트 정렬이 깨져요. @alignCast 안전 검사가 이를 감지하고 incorrect alignment 패닉을 내며 프로세스가 종료돼요. 이렇게 컴파일 타임에는 컴파일 에러로, 런타임에는 패닉으로 잘못된 정렬을 막아주는 게 Zig의 정렬 안전 검사예요.
더 알아보기
@alignCast— 포인터를 더 큰 정렬의 포인터로 바꾸면서 정렬 안전 검사를 넣는 내장 함수- Alignment — 정렬의 기본 개념과
@alignOf사용법