unsigned int의 오버플로우
C’s unsigned integer types are ‘‘modulo’’ in the LIA−1 sense in that overflows or out-of-bounds results silently wrap.
- C99 스펙
unsigned int는 오버플로우가 없습니다.
표현 가능한 개수로 나눈 나머지가 된다고 정의되어 있습니다.
#include <stdio.h>
int main(void)
{
unsigned int num1;
unsigned int num2;
const char *result;
scanf("%d %d", &num1, &num2);
result = "!(%u > %u && %u > %u)";
if (num1 > num2 && num1 + 1 > num2)
result = "%u > %u && %u > %u";
printf(result, num1, num2, num1 + 1, num2);
return (0);
}
C
복사
입력으로 4294967295 0를 넣으면 !(4294967295 > 0 && 0 > 0)가 되는 것을 볼 수 있습니다.
-O3 플래그로 컴파일러 최적화를 활성화해도 마찬가지입니다. unsigned int는 원래 이렇게 동작합니다.
signed int의 오버플로우
C/C++에서 signed int의 오버플로우는 정의되지 않았습니다.
왜 아직도 정의되지 않았는지 의아할 수 있는데 컴파일러 최적화 때문입니다.
수학적으로 볼 때 A가 B보다 크다면, A + 1도 당연히 B보다 클 것입니다.
이 점을 이용해 컴파일러가 최적화를 할 수도, 안 할 수도 있습니다.
#include <stdio.h>
int main(void)
{
int num1;
int num2;
const char *result;
scanf("%d %d", &num1, &num2);
result = "!(%d > %d && %d > %d)";
if (num1 > num2 && num1 + 1 > num2)
result = "%d > %d && %d > %d";
printf(result, num1, num2, num1 + 1, num2);
return (0);
}
C
복사
문제가 없을 수도 있습니다. 일단 여기서는 -O3 플래그로 최적화를 활성화 해 보겠습니다.
2147483647 0를 입력하면 2147483647 > 0 && -2147483648 > 0가 출력됩니다.
(num1 + 1 > num2), 즉 -2147483648 > 0이 true라는 뜻이죠.
하지만 컴파일러는 이 식을 (num1 > num2)으로 최적화할 수 있습니다.
undefined behavior의 위험성?
undefined behavior는 말 그대로 정의되지 않은 동작이지 반드시 다르게 동작하는 것이 아닙니다.
어떻게 동작할 것이라고도 기대해서는 안 됩니다.
어떤 결과가 나올지 모르기 때문에, undefined behavior에 의존하면 안 됩니다.
printf(”%s”, NULL)은 대부분 (null)을 출력하지만 C 표준에서는 이에 대해 정의하지 않습니다.
glibc의 경우에는 문서에 printf(”%s”, NULL)가 (null)을 출력한다는 내용이 있습니다.
glibc처럼 정의된 경우가 아니라면 printf(”%s”, NULL)는 undefined behavior입니다.
printf(”%s”, NULL)가 오류를 내지 않을 것이라고 기대해서는 안 됩니다.
실제로는 오류가 날 수 있고, 오류가 나도 그 printf는 잘못한 것이 없습니다.
Makefile에서 re: fclean all로 쓰는 것 역시 undefined behavior에 의존하는 것입니다.