35. checked / unchecked 컨텍스트와 정수 오버플로의 내부 동작
난이도 중내 리뷰 · C#
해설 · C#
해설 — checked / unchecked 컨텍스트와 정수 오버플로의 내부 동작
난이도: 중
요약
C#의 정수 오버플로는 기본적으로 unchecked(감싸기, wrap) 이다(프로젝트 옵션으로
바꾸지 않는 한). checked는 오버플로 시 OverflowException을 던진다. 단
컴파일 시점에 계산되는 상수식은 기본이 checked 라서, 상수 오버플로는 컴파일
에러가 된다. 골드·재화 같은 값은 조용한 wrap이 치명적이므로 checked 또는 더 넓은
타입 + 범위 검증으로 보호해야 한다.
핵심 개념 1: 런타임 오버플로 (RuntimeOverflow 출력)
unchecked: -2147483648 // (A) int.MaxValue + 1 이 감싸여 int.MinValue
checked: OverflowException // (B) checked 는 예외
- (A) 기본 컨텍스트에서
max + 1은 2의 보수 연산이 그대로 감싸여int.MinValue(-2147483648)가 된다. 예외 없음. - (B)
checked(...)안에서는 오버플로가 감지되어OverflowException. - IL 수준 차이: 일반 덧셈은
add명령으로 컴파일된다.checked이면add.ovf(overflow-checking add)로 컴파일되어, CPU의 오버플로 플래그를 확인하고 넘치면 예외를 던진다. 즉 checked/unchecked는 어떤 IL 산술 명령을 내보내는지의 차이다 (add/add.ovf,mul/mul.ovf,conv/conv.ovf). 런타임 비용은 분기 하나 수준으로 작다.
핵심 개념 2: 상수식은 기본이 checked (CompileTimeConst)
- (C)
const int a = int.MaxValue + 1;→ 컴파일 에러. 컴파일 타임에 평가되는 상수식은 기본적으로 checked로 취급된다(언어 명세). 오버플로가 확정적이므로 컴파일러가 CS0220 "operation overflows at compile time"으로 거부한다. - (D)
const int b = unchecked(int.MaxValue + 1);→ 컴파일 성공. 명시적으로unchecked를 씌우면 감싸기를 허용해b == int.MinValue로 컴파일된다. - 요점: 런타임 산술의 기본은 unchecked, 컴파일타임 상수의 기본은 checked 로 방향이 반대다. 이 비대칭이 헷갈림의 핵심.
핵심 개념 3: 전역 옵션과 지역 컨텍스트
- 프로젝트의
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>(csc의/checked+)를 켜면 모든 비상수 산술의 기본이 checked가 된다. 성능 민감 루프만 국소적으로unchecked { ... }로 되돌린다. - 지역
checked/unchecked블록·식은 항상 전역 옵션을 국소적으로 오버라이드한다. 가장 안쪽 컨텍스트가 이긴다. - (E)
AddGold의 문제:current + delta는int + int라 int로 계산된 뒤long으로 승격된다. 즉 int 범위에서 먼저 오버플로가 나고(감싸짐) 그 감싼 값이 long이 된다. 반환 타입이 long이어도 소용없다.public static long AddGold(int current, int delta) { return (long)current + delta; // long 산술로 승격 후 더함 → int 오버플로 없음 // 또는 checked((int)...) 로 상한 초과를 예외/클램프 처리 }
흔한 오해·함정
- "C#은 오버플로하면 예외" → 기본은 아니다. 감싸기다. Java와 같은 기본 동작.
예외를 원하면
checked또는 전역 옵션. - "반환 타입을 long으로 하면 안전" → 피연산자 타입이 int면 연산이 int로 먼저 일어나 그 시점에 오버플로. 승격은 연산 후가 아니라 피연산자를 미리 넓혀야 한다.
- "상수라서 wrap될 것" → 상수식은 기본 checked라 오히려 컴파일 에러. 방향이 반대.
decimal은 항상 오버플로 시 예외(checked/unchecked 무관).float/double은 오버플로가 아니라Infinity가 된다(정수 규칙과 다름).
면접 포인트
- "C#의 기본 오버플로 동작은?" → 런타임 산술 기본 unchecked(wrap), 컴파일타임 상수식 기본 checked(오버플로면 컴파일 에러). 비대칭을 정확히 말할 것.
- "checked의 IL/비용은?" →
add.ovf/mul.ovf/conv.ovf명령으로 오버플로 플래그 검사 + 예외. 비용은 작고, 재화·시퀀스 등 정확성이 중요한 곳엔 켜는 게 안전. - "재화 합산 안전하게?" → 피연산자를 미리 long으로 넓히거나 checked로 상한 초과를 탐지해 예외/클램프. int 범위 wrap이 재화 복사·소멸로 직결되기 때문.