C에서 자동 형 변환은 "일반 산술 변환" 원칙에 따라 작동합니다. 부호가 다른 숫자(signed/unsigned)가 표현식에 참여할 때 다음 규칙에 따라 변환이 발생합니다:
위험한 산술의 예:
int a = -1; // signed unsigned int b = 1; printf("%d\n", a < b); // 항상 false, a가 매우 큰 unsigned로 변환되기 때문
결과: -1이 unsigned로 변환되면 매우 큰 양의 정수가 됩니다.
기억해야 할 중요한 점:
질문: 표현식 (int)(unsigned)-1은 어떤 결과를 반환할까요?
예상치 못한 잘못된 답변: "-1, 왜냐하면 -1을 int로 변환하기 때문입니다."
올바른 답변:
표현식 (unsigned)-1에서 먼저 -1이 unsigned로 변환됩니다(32비트 플랫폼에서는 0xFFFFFFFF가 됨), 그 다음 다시 signed int로 변환됩니다. 이는 구현에 따라 다르지만, 대부분의 경우 다시 -1이 됩니다(2의 보수를 사용할 경우). 그러나 더 정확히 말하면: 결과는 signed 숫자의 표현 표준에 따라 달라지지만, 대부분의 구현에서는 -1이 될 것입니다.
예:
int x = (int)(unsigned)-1; // x == -1 대부분의 플랫폼에서
이야기
문자열 처리기에서 크기 비교 함수를 사용했는데, 문자열 길이가 음수일 수 있는 경우 프로그램에서 오류를 알리지 않았습니다. 그러나 길이는 size_t(unsigned) 유형이었고,
if(length < 0)코드의 비교는 항상 false를 반환하여 무한 루프와 메모리 오버플로를 초래했습니다.
이야기
프로토콜 파싱 시 네트워크 패킷이 unsigned 필드를 포함했으나 로컬 변수는 signed였습니다. 일부 값을 처리할 때 unsigned 오버플로로 인해 패킷 길이 계산이 잘못되어 버퍼 오버플로 취약점이 발생했습니다.
이야기
로그의 날짜 비교 모듈에서 날짜를 unsigned int로 저장했으나 int로 날짜 범위를 검색했습니다. 특정 경계 값에서는 예상했던 예외가 발생하는 대신 잘못된 필터링 결과로 인해 중요한 로그가 손실되었습니다.