예상치 못한 untyped int
의 동작, 왜 이런 일이?
Go에서 const
로 선언된 정수는 기본적으로 "untyped" 상태입니다.
즉, 특정한 타입(int
, int64
등)이 정해지지 않은 상태로, 사용되는 문맥에 따라 타입이 결정됩니다.
이런 특성 때문에 32비트 환경과 64비트 환경에서 다르게 동작하거나,
컴파일 에러가 발생하는 경우가 있어 주의가 필요합니다.
이 글에서는 untyped int
가 어떻게 동작하는지,
그리고 예상치 못한 동작이 발생하는 이유를 예제와 함께 알아보겠습니다.
1. untyped int
의 기본적인 동작
다음과 같은 코드가 있다고 해볼까요?
package main
import "log"
const x = 9876543210
func main() {
log.Printf("%v", x) // 32비트 환경에서는 컴파일 에러 발생
}
이 코드는 64비트 환경에서는 정상적으로 동작합니다.
하지만 32비트 환경에서는 컴파일 에러가 발생합니다.
왜 이런 일이 발생할까요?
x
는untyped int
상태입니다.log.Printf("%v", x)
에서x
는any
(interface{}
)로 전달됩니다.- 하지만 Go에서는
untyped int
를 자동으로int
로 변환하려고 시도합니다. - 문제는
9876543210
이 32비트int
범위를 초과하기 때문에 컴파일 에러 발생!
즉, Go에서는 untyped int
가 기본적으로 int
로 변환되므로,
32비트 환경에서는 int
의 최대값(2,147,483,647
)을 초과하는 숫자는 사용할 수 없습니다.
이를 해결하려면 타입을 명시적으로 지정해야 합니다.
const x int64 = 9876543210 // 명시적으로 int64 지정
log.Printf("%v", x) // 32비트 환경에서도 정상 동작
2. 연산에 따라 달라지는 untyped int
의 동작
untyped int
는 문맥에 따라 타입이 결정됩니다.
그렇다면 연산을 수행하면 어떻게 될까요?
비트 시프트 연산 (Shift 연산자 >>
)
const x = 9876543210
const s = int(3)
func main() {
log.Printf("%v", x>>s) // ✅ 정상 동작
}
x
는untyped int
이지만,
비트 시프트(>>
) 연산에서는untyped
상태가 유지됩니다.- 따라서 32비트 환경에서도 컴파일 에러 없이 동작합니다.
나눗셈 연산 (/
)
const x = 9876543210
const s = int(1 << 3)
func main() {
log.Printf("%v", x/s) // ❌ 32비트 환경에서 컴파일 에러 발생
}
s
는int
타입입니다.untyped int
인x
가int
와 함께 연산되면서 자동으로int
로 변환됩니다.- 하지만
x
는int
의 범위를 초과하기 때문에 컴파일 에러 발생!
즉, 비트 시프트 연산은 안전하지만, 나눗셈 연산은 위험할 수 있습니다.
해결 방법
const x int64 = 9876543210
const s = int64(1 << 3)
func main() {
log.Printf("%v", x/s) // ✅ 정상 동작
}
명시적으로 int64
타입을 지정하면 안전하게 동작합니다.
3. switch
문에서 예상치 못한 변환
switch
문에서 untyped int
사용
func main() {
switch 9876543210 { // ❌ 32비트 환경에서 컴파일 에러 발생
case 1:
log.Println("one")
default:
log.Println("not one")
}
}
switch
문에서9876543210
은untyped int
입니다.- 하지만
switch
는 내부적으로 기본 타입(int
)로 변환하려고 합니다. - 32비트 환경에서는
int
의 범위를 초과하므로 컴파일 에러 발생!
해결 방법
func main() {
switch int64(9876543210) { // ✅ int64로 변환
case 1:
log.Println("one")
default:
log.Println("not one")
}
}
int64
로 변환하면 정상적으로 동작합니다.
case
값의 타입이 다를 때
func main() {
switch 9876543210 { // ❌ 32비트 환경에서 에러 발생
case int64(1): // mismatched types int64 and int
log.Println("one")
default:
log.Println("not one")
}
}
9876543210
이 기본적으로int
로 변환되고,case int64(1)
은int64
타입이므로 타입 불일치 오류 발생!
해결 방법
func main() {
switch int64(9876543210) { // ✅ int64로 변환
case int64(1):
log.Println("one")
default:
log.Println("not one")
}
}
switch
의 값과 case
값을 같은 타입으로 맞춰야 합니다.
4. 컴파일 에러 없이 위험한 동작
컴파일 에러가 나지 않아도 환경에 따라 다르게 동작하는 코드가 있을 수 있습니다.
const x = 100000001
s := 10
func main() {
log.Println((x << s) % 1000) // 64비트: 24, 32비트: -80
}
- 64비트 환경에서는
24
가 출력되지만, - 32비트 환경에서는
-80
이 출력됩니다. - 같은 코드인데 결과가 다르게 나오는 위험한 상황! 😨
이 문제를 해결하려면?
const x int64 = 100000001
s := 10
func main() {
log.Println((x << s) % 1000) // ✅ 모든 환경에서 24 출력
}
타입을 명확하게 지정하면 환경에 관계없이 예상한 값이 나옵니다.
5. Zig 언어와 비교
Go 말고도 Zig라는 언어에서도 comptime_int
라는 개념이 있습니다.
하지만 Zig는 컴파일 시간에만 존재하는 정수 타입이므로,
print
같은 함수에 넘겨도 문제가 발생하지 않습니다.
const x = 987654321098765432109876543210;
try stdout.print("{}\n", .{x}); // ✅ 정상 동작
Go와 다르게 int
로 변환되는 과정이 없어서 안전합니다.
결론: 안전한 코드 작성법
- 큰 숫자는 명시적으로
int64
나uint64
타입을 지정하자. - 연산을 할 때 다른 타입(
int
vsint64
)을 섞지 말자. switch
문에서는case
의 타입과switch
값의 타입을 맞추자.- 환경(32비트 vs 64비트)에 따라 결과가 달라질 수 있는 코드를 주의하자.
Go의 untyped int
는 편리하지만, 예상치 못한 버그를 유발할 수 있습니다.
위의 원칙을 잘 기억하고 안전한 코드를 작성하세요!
'Go' 카테고리의 다른 글
Steam(스팀)이 Go(고) 런타임을 망가뜨린다고? 게임 개발, 이제 Go로도 가능한가? (0) | 2025.02.09 |
---|---|
sqlx와 PostgreSQL 타임존 완벽 정복! (0) | 2025.01.12 |
Go에서 구조체 패킹(structure packing) 이해하기 (0) | 2024.12.28 |
Go 언어의 reflect 패키지: 첫걸음부터 실전 활용까지 (1) | 2024.11.08 |
Go 언어의 리플렉션, 제대로 활용하는 방법! (1) | 2024.10.30 |