37. 정수 승격과 통상 산술 변환, signed/unsigned 혼합의 함정
난이도 중내 리뷰 · C++
해설 · C++
해설 — 정수 승격과 통상 산술 변환, signed/unsigned 혼합의 함정
난이도: 중상
요약
이항 산술/비교 연산은 피연산자를 공통 타입으로 맞춘 뒤 수행한다. 규칙은 두 단계: (1) 정수 승격 — bool/char/short 등 int 보다 작은 정수는 먼저 int(값이 다 안 들어가면 unsigned int)로 승격. (2) 통상 산술 변환 — 남은 두 타입 중 랭크·부호에 따라 공통 타입 결정. 같은 랭크에서 signed 와 unsigned 가 섞이면 signed 가 unsigned 로 변환된다. 이 마지막 규칙이 음수를 거대한 양수로 바꿔 비교를 뒤집는다.
예측 출력:
0 // (1) i < u
300 // (2) c1 + c2
1 // (3) s > 0u
4 // (4) sizeof(true+true)
2 // (5) true + true
4294967286 // (6) a + b
1 // (7) 'a' < 'b'
핵심 개념 · 단계별 내부 동작
- (1)
i < u→ 0(false):int -1과unsigned 1. 같은 랭크라int가unsigned로 변환 →-1은4294967295.4294967295 < 1은 거짓. 직관("-1 이 1 보다 작다")과 반대다. 컴파일러가-Wsign-compare로 경고하는 대표 사례. - (2)
c1 + c2→ 300:unsigned char두 개는 연산 전에int로 승격된다.200+100=300을int로 계산 → 오버플로 없이 300. (만약 결과를 다시unsigned char에 담았다면 300 % 256 = 44 로 잘렸을 것.) - (3)
s > 0u→ 1(true):short -1은int로 승격(-1) 후, 상대가unsigned int라 통상 산술 변환으로-1→4294967295.4294967295 > 0참. 음수short가 unsigned 와 비교되며 거대 양수로 바뀐다. - (4)
sizeof(true+true)→ 4:bool은int로 승격되어int+int→ 결과 타입은int(4바이트).sizeof는 타입 크기이지 값이 아니다. - (5)
true + true→ 2: 승격된1 + 1= 2.bool이 아니라int결과. - (6)
a + b→ 4294967286:unsigned 10+int -20.int가unsigned로 변환 →-20은4294967276.4294967276 + 10 = 4294967286(2^32 미만이라 랩 없음). 결과 타입이unsigned라 그대로 출력. - (7)
'a' < 'b'→ 1: 문자 리터럴은char(값 97, 98)로int승격 후 비교 → 참.
흔한 오해·함정
- "음수는 항상 양수보다 작다"는 signed/unsigned 혼합에서 깨진다.
size()는 대개size_t(unsigned)라if (i < v.size())에서i가int이고 음수면 참이 아닌 거짓이 되어 루프/검증이 어긋난다. - 작은 정수 산술은
int로 승격되므로unsigned char덧셈이 곧바로 wrap 하지 않는다 — wrap 은 결과를 좁은 타입에 다시 담을 때 발생. sizeof(expr)는 피연산자를 평가하지 않고 타입만 본다.
면접 포인트
- 게임 서버 패킷 파싱의 실전 버그: 길이 필드를
int len으로 읽고if (len < buf.size())로 검증하면, 조작된 음수len이buf.size()(size_t) 와 비교되며 거대 양수로 바뀌어 검증을 통과 → OOB. 길이·인덱스는 부호·범위를 명시적으로 검사하고, 비교 전에 같은 부호 타입으로 정규화해야 한다. -Wsign-compare/-Wconversion경고를 켜고 무시하지 말 것.- 정수 승격(1단계)과 통상 산술 변환(2단계)을 분리해 설명할 수 있어야 한다 — 둘은 다른 규칙이다.