이것이 정의되지 않은 동작 인 이유는 무엇입니까?
이 질문에 대한 나의 대답 은 다음과 같습니다.
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143 <= 286331153;
}
VS2008 컴파일러를 사용하여 내 컴퓨터에서 완벽하게 작동했지만 여기서는 전혀 작동하지 않습니다.
누구든지 아이디어가 있습니까? 왜 다른 컴파일러에서 다른 결과를 얻습니까? unsigned
오버플로는 정의되지 않은 동작이 아닙니다.
중요 사항 : 일부 테스트 후 나머지 부분을 15로 나누는 것보다 더 빠릅니다. (그러나 모든 컴파일러에 적용되지는 않음)
정의되지 않은 동작이 아니라 C89와 C99 사이의 C 언어 표준의 주요 변경 사항입니다.
C89에서 4008636143 같은 상수 정수 그건에 맞지 않는 int
또는 long int
그러나이에 적합 할 unsigned int
부호이지만, C99, 그들은 하나 있습니다 long int
또는 long long int
(하나의 값을 저장할 수있는 작은 일 중에 따라 다름). 결과적으로 모든 표현식은 64 비트를 사용하여 평가되어 오답이됩니다.
Visual Studio는 C89 컴파일러이므로 C89 동작이 발생하지만 Ideone 링크는 C99 모드에서 컴파일됩니다.
다음을 사용하여 GCC로 컴파일하면 더 분명해집니다 -Wall
.
test.c: In function ‘divisible15’:
test.c:8:3: warning: this decimal constant is unsigned only in ISO C90
C89 §3.1.3.2에서 :
정수 상수의 유형은 해당 값이 표시 될 수있는 해당 목록의 첫 번째입니다. 비접 미 십진수 : int, long int, unsigned long int; 접미사가없는 8 진수 또는 16 진수 : int, unsigned int, long int, unsigned long int; [...]
C99 §6.4.4.1 / 5-6 :
5) 정수 상수의 유형은 해당 값을 나타낼 수있는 해당 목록 중 첫 번째입니다.
Suffix | Decimal Constant | Octal or Hexadecimal Constant -------+------------------+------------------------------ none | int | int | long int | unsigned int | long long int | long int | | unsigned long int | | long long int | | unsigned long long int -------+------------------+------------------------------ [...]
6) 정수 상수가 목록에서 어떤 유형으로도 표현 될 수없는 경우 확장 정수 유형이 값을 나타낼 수 있으면 확장 정수 유형을 가질 수 있습니다. 상수 목록에있는 모든 유형이 서명 된 경우 확장 정수 유형이 서명되어야합니다. [...]
완전성을 위해 C ++ 03은 정수 상수가 너무 커서 long int
. C ++ 03 §2.13.1 / 2에서 :
정수 리터럴의 유형은 형식, 값 및 접미사에 따라 다릅니다. 십진수이고 접미사가없는 경우 값을 나타낼 수있는 첫 번째 유형이 있습니다.
int
,long int
; 값을로 표현할 수없는long int
경우 동작이 정의되지 않습니다. 이 진수 16 진수이고, 접미사가없는 경우, 그 값을 표시 할 수있는 이러한 유형의 제 갖는다 :int
,unsigned int
,long int
,unsigned long int
. [...]
C ++ 11 동작은 C99와 동일합니다 (C ++ 11 §2.14.2 / 3 참조).
하나 C89, C99, C ++ 03 및 C ++ (11)로 컴파일 할 때 코드 동작합니다 일관, 간단한 수정과를 부기 부호없는 정수 4,008,636,143 수 있도록하는 것입니다 있는지 확인하기 u
로를 4008636143u
.
int
상수 를 사용하기 때문에 컴파일러는 코드를 바로 가기 위해 정의되지 않은 오버플로를 "사용"할 수 있습니다. 아래와 같이 U로 수정하면 "작동"합니다.
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143u <= 286331153u;
}
테스트 :
#include <iostream>
inline bool divisible15a(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
// return x * 4008636143 <= 286331153;
return x * 4008636143u <= 286331153;
}
inline bool divisible15b(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
// return x * 4008636143 <= 286331153;
return x * 4008636143 <= 286331153;
}
int main()
{
for(unsigned int i = 0; i < 100; i++)
{
if (divisible15a(i))
{
std::cout << "a:" << i << std::endl;
}
if (divisible15b(i))
{
std::cout << "b:" << i << std::endl;
}
}
}
산출:
a:0
b:0
a:15
a:30
a:45
a:60
a:75
a:90
암호:
_Z12divisible15aj:
.LFB1192:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
imull $-286331153, %eax, %eax
cmpl $286331153, %eax
setbe %al
popq %rbp
ret
_Z12divisible15bj:
.LFB1193:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %edx
movl $4008636143, %eax
imulq %rdx, %rax
cmpq $286331153, %rax
setle %al
popq %rbp
ret
그렇습니다. 저는 이것이 32 비트 정수에 맞지 않는다는 Carl / Adam의 분석에 동의하므로 long
또는 long long
.
참고 URL : https://stackoverflow.com/questions/18706859/why-is-this-an-undefined-behavior
'program story' 카테고리의 다른 글
JSON 배열을 Python 목록으로 변환 (0) | 2020.12.08 |
---|---|
Eclipse에서 모든 중단 점을 보는 방법은 무엇입니까? (0) | 2020.12.08 |
Log4j2 구성에서 "상태"는 무엇을 의미합니까? (0) | 2020.12.08 |
Integer와 Fixnum의 차이점은 무엇입니까? (0) | 2020.12.08 |
WPF 응용 프로그램에 나타나는 이상한 블랙 박스 (0) | 2020.12.08 |