정수
정수 (Integers)
Zig에서 정수를 어떻게 다루는지, 리터럴부터 타입까지 한 번에 정리해 볼게요. 여기서는 정수 리터럴을 쓰는 법과, 런타임에서 실제 정수 값이 어떻게 검증되는지 두 갈래로 나눠서 설명해요. 정수는 프로그램에서 가장 흔하게 만나는 값이라서, 이 구간을 확실히 잡아 두면 뒤에 나올 부동소수점부터 포인터까지 훨씬 수월해져요.
본문
Integer Literals
정수 리터럴은 그냥 숫자만 쓰는 게 아니라, 진법을 나타내는 접두사(prefix)를 붙여서 여러 방식으로 표현할 수 있어요. 10진수는 그냥 숫자를 쓰면 되고, 16진수는 0x, 8진수는 0o, 2진수는 0b를 앞에 붙여요. 아래 코드에서 각 진법을 어떻게 쓰는지 확인해 볼게요.
const decimal_int = 98222;
const hex_int = 0xff;
const another_hex_int = 0xFF;
const octal_int = 0o755;
const binary_int = 0b11110000;
// underscores may be placed between two digits as a visual separator
const one_billion = 1_000_000_000;
const binary_mask = 0b1_1111_1111;
const permissions = 0o7_5_5;
const big_address = 0xFF80_0000_0000_0000;
여기서 재미있는 점은 16진수 0xff와 0xFF가 같은 값이라는 거예요. 대문자든 소문자든 진법 표기에는 아무 상관이 없어요. 또, one_billion처럼 숫자 사이에 밑줄(_)을 넣을 수 있는데, 이건 값에는 전혀 영향을 주지 않고 그저 눈으로 읽기 좋게 구분해 주는 자리표시자일 뿐이에요. 1_000_000_000은 말 그대로 1000000000과 완전히 같은 값이에요.
Runtime Integer Values
정수 리터럴 자체는 크기 제한이 아예 없어요. 그러니까 한도를 걱정하지 않고 큰 숫자를 그대로 써도 되고, 만에 하나 잘못된 동작(Illegal Behavior)이 생길 여지가 있으면 컴파일러가 바로 잡아 줘요. 예를 들어 리터럴을 통해 표현할 수 있는 범위를 넘는 값을 쓴다거나 하는 상황이 그런 거예요.
그런데 이건 리터럴일 때 이야기예요. 컴파일 타임에 값이 정해져 있지 않아서 런타임에만 알 수 있는 정수라면 얘기가 달라져요. 그 값은 반드시 크기가 정해진 타입을 가져야 하고, 안전 검사(safety-checked)가 적용되는 잘못된 동작에 노출돼요. 아래 함수를 볼게요.
fn divide(a: i32, b: i32) i32 {
return a / b;
}
이 함수에서 a와 b는 값이 런타임에만 알려져요. 그런 값으로 나눗셈을 하다 보면 두 가지 문제가 생길 수 있어요. 하나는 결과가 i32의 범위를 넘어서는 Integer Overflow(정수 오버플로우), 또 하나는 b가 0이 되어 버리는 Division by Zero(0으로 나누기) 죠. 컴파일 타임에 값을 알 수 없으니까 컴파일러가 미리 다 걸러 주지 못하고, 실제로 실행될 때 안전 검사가 발동하는 거예요.
오버플로우에 관해서는 +, - 같은 보통 연산자는 잘못된 동작(Illegal Behavior)을 일으켜요. 그래서 Zig는 모든 대상에서 wrapping(래핑)과 saturating(새추레이팅) 연산을 위한 별도의 연산자를 제공해 줘요. +%와 -%는 wrapping 산술을 하고, +|와 -|는 saturating 산술을 해요. 오버플로우를 신경 쓰지 않고 그냥 비트 패턴만 유지하고 싶다면 +%를, 값을 최대/최소 범위에 딱 맞게 잘라 버리고 싶다면 +|를 쓰면 돼요.
정수 타입은 비트 폭(bit-width)을 자유롭게 지정할 수도 있어요. i 또는 u 뒤에 숫자를 붙이면 되는데, i7은 부호 있는 7비트 정수를 가리켜요. i는 signed(부호 있음), u는 unsigned(부호 없음)를 뜻해요. 정수 타입의 최대 비트 폭은 65535까지 가능하고요. 부호 있는 정수 타입은 two's complement(2의 보수) 표현을 사용해요.
더 알아보기
- Wrapping Operations — wrapping 산술 연산을 더 자세히 보고 싶을 때 찾아보세요.
+%나-%가 어떤 사고방식으로 설계됐는지 여기서 이어서 확인할 수 있어요.