GCC가있는 x86에서 정수 오버플로가 무한 루프를 일으키는 이유는 무엇입니까?


129

다음 코드는 GCC에서 무한 루프에 들어갑니다.

#include <iostream>
using namespace std;

int main(){
    int i = 0x10000000;

    int c = 0;
    do{
        c++;
        i += i;
        cout << i << endl;
    }while (i > 0);

    cout << c << endl;
    return 0;
}

그래서 여기 거래는 다음과 같습니다 오버 플로우가 기술적으로 정의되지 않은 동작입니다 정수를 체결했다. 그러나 x86의 GCC는 x86 정수 명령어를 사용하여 정수 산술을 구현합니다.

따라서 정의되지 않은 동작이라는 사실에도 불구하고 오버플로를 감쌀 것으로 예상했습니다. 그러나 그것은 사실이 아닙니다. 그래서 내가 무엇을 놓쳤습니까?

나는 이것을 사용하여 이것을 컴파일했다 :

~/Desktop$ g++ main.cpp -O2

GCC 출력 :

~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0

... (infinite loop)

최적화가 비활성화되면 무한 루프가 없으며 출력이 정확합니다. Visual Studio는이를 올바르게 컴파일하고 다음 결과를 제공합니다.

올바른 출력 :

~/Desktop$ g++ main.cpp
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
3

다른 변형은 다음과 같습니다.

i *= 2;   //  Also fails and goes into infinite loop.
i <<= 1;  //  This seems okay. It does not enter infinite loop.

모든 관련 버전 정보는 다음과 같습니다.

~/Desktop$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ..

...

Thread model: posix
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) 
~/Desktop$ 

질문은 GCC의 버그입니까? 아니면 GCC가 정수 산술을 처리하는 방법에 대해 잘못 이해 했습니까?

*이 버그가 C로 재생산된다고 가정하기 때문에이 C에도 태그를 지정하고 있습니다 (아직 확인하지 않았습니다).

편집하다:

다음은 루프 어셈블리입니다. (올바르게 인식 한 경우)

.L5:
addl    %ebp, %ebp
movl    $_ZSt4cout, %edi
movl    %ebp, %esi
.cfi_offset 3, -40
call    _ZNSolsEi
movq    %rax, %rbx
movq    (%rax), %rax
movq    -24(%rax), %rax
movq    240(%rbx,%rax), %r13
testq   %r13, %r13
je  .L10
cmpb    $0, 56(%r13)
je  .L3
movzbl  67(%r13), %eax
.L4:
movsbl  %al, %esi
movq    %rbx, %rdi
addl    $1, %r12d
call    _ZNSo3putEc
movq    %rax, %rdi
call    _ZNSo5flushEv
cmpl    $3, %r12d
jne .L5

10
에서 생성 된 어셈블리 코드를 포함하면 훨씬 더 답할 수 있습니다 gcc -S.
Greg Hewgill

어셈블리는 놀랍도록 길다. 여전히 편집해야합니까?
Mysticial

루프와 관련된 부품 만 사용하십시오.
Greg Hewgill

12
-1. 이것은 엄격하게 말해서 정의되지 않은 동작이라고 말하고 이것이 정의되지 않은 동작인지 묻습니다. 그래서 이것은 실제 질문이 아닙니다.
Johannes Schaub-litb

8
@ JohannesSchaub-litb 의견을 보내 주셔서 감사합니다. 내 입장에서 아마 나쁜 말씨. 나는 당신의 undownvote를 얻는 방법을 명확히하기 위해 최선을 다할 것입니다 (그리고 나는 그에 따라 질문을 편집 할 것입니다). 기본적으로 나는 그것이 UB라는 것을 알고 있습니다. 그러나 x86의 GCC는 x86 정수 명령어를 사용한다는 것을 알고 있습니다. 따라서 나는 그것이 UB 임에도 불구하고 랩핑을 기대 했다. 그러나 그것은 아니었고 저를 혼란스럽게했습니다. 따라서 질문입니다.
Mysticial

답변:


178

표준에 정의되지 않은 동작이라고 표시되면 표준 을 의미합니다 . 무슨 일이든 일어날 수 있습니다. "Anything"에는 "보통 정수가 둘러싸지만 가끔 이상한 일이 발생합니다"가 포함됩니다.

예, x86 CPU에서 정수는 일반적으로 예상대로 줄 바꿈됩니다. 이것은 예외 중 하나입니다. 컴파일러는 정의되지 않은 동작을 유발하지 않으며 루프 테스트를 최적화합니다. 당신이 정말로 랩 어라운드를 원하는 경우, 통과 -fwrapvg++또는 gcc때 컴파일; 이렇게하면 잘 정의 된 (2 보완) 오버 플로우 의미론이 제공되지만 성능이 저하 될 수 있습니다.


24
오 와우. 나는 몰랐다 -fwrapv. 이것을 지적 해 주셔서 감사합니다.
Mysticial

1
우발적 인 무한 루프를 감지하는 경고 옵션이 있습니까?
Jeff Burdges

5
나는 -Wunsafe - 루프 최적화는 여기에 언급 발견 : stackoverflow.com/questions/2982507/...
제프 Burdges

1
-1 "예, x86 CPU에서 정수는 보통 예상대로 줄 바꿈합니다." 그건 틀렸어요. 하지만 미묘합니다. 내가 기억하는 것처럼 오버플로에 빠질 수는 있지만, 우리가 여기서 말하는 것은 아니며 , 결코 본 적이 없습니다. 그 외에는 x86 bcd 연산 (C ++에서는 허용되지 않는 표현)을 무시하면 x86 정수 연산은 항상 2의 보수이므로 래핑됩니다. x86 정수 연산의 속성에 대해 g ++ 결함 (또는 매우 비실용적이고 넌센스) 최적화를 착각하고 있습니다.
건배와 hth. -Alf

5
@ Cheersandhth.- 알프, 'x86 CPU에서'는 'C 컴파일러를 사용하여 x86 CPU를 개발할 때'를 의미합니다. 정말 철자를 써야합니까? 분명히 컴파일러와 GCC에 대한 모든 이야기는 어셈블러로 개발하는 경우 관련이 없습니다.이 경우 정수 오버플로에 대한 의미가 실제로 잘 정의되어 있습니다.
bdonlan

18

간단합니다. 정의되지 않은 동작, 특히 최적화 ( -O2)가 설정된 경우에는 모든 일이 발생할 수 있습니다.

코드는 -O2스위치 없이 예상대로 동작 합니다.

그건 그렇고 icl과 tcc에서는 꽤 잘 작동하지만 그런 것들에 의존 할 수는 없습니다 ...

에 따르면 , GCC 최적화는 실제로 오버 플로우 정수 서명 이용한다. 이것은 "버그"가 의도적으로 설계된 것을 의미합니다.


컴파일러가 정의되지 않은 동작을 위해 모든 것의 무한 루프를 선택한다는 사실은 다소 짜증납니다.
Inverse

27
@Inverse : 동의하지 않습니다. 정의되지 않은 동작으로 무언가를 코딩했다면 무한 루프를 위해기도하십시오. 더 쉽게 감지 할 수 있도록 ...
Dennis

컴파일러가 적극적으로 UB를 찾고 있다면 깨진 코드를 과도하게 최적화하는 대신 예외를 삽입하지 않겠습니까?
Inverse

15
@Inverse : 컴파일러가 정의되지 않은 동작을 적극적으로 찾지 않고 발생하지 않는다고 가정합니다. 이를 통해 컴파일러는 코드를 최적화 할 수 있습니다. 예를 들어, 컴퓨팅 대신에 서명 된 오버플로가 발생하지 않으면 항상 true 이므로 for (j = i; j < i + 10; ++j) ++k;그냥 설정 됩니다. k = 10
Dennis

@Inverse 컴파일러는 아무것도 선택하지 않았다. 코드에 루프를 작성했습니다. 컴파일러는 그것을 발명하지 않았습니다.
궤도에서 가벼움 경주

13

여기서주의해야 할 것은 C ++ 프로그램이 C ++ 추상 머신 (일반적으로 하드웨어 명령어를 통해 에뮬레이트 됨)을 위해 작성되었다는 것입니다. x86 용으로 컴파일 하고 있다는 것은 이것이 정의되지 않은 동작을 가지고 있다는 사실과 는 전혀 관련이 없습니다.

컴파일러는 정의되지 않은 동작의 존재를 사용하여 최적화를 향상시킵니다 (이 예에서와 같이 루프에서 조건부를 제거하여). 기계 코드가 실행될 때 C ++ 추상 기계에 의해 요구되는 결과를 생성해야한다는 요구 사항 외에, C ++ 레벨 구조와 x86 레벨 기계 코드 구조 사이에는 보장되거나 유용한 매핑이 없습니다.



3

사람들 제발 정의되지 않은 동작이는 것을 정확히 정의되지 . 그것은 무슨 일이든 일어날 수 있다는 것을 의미합니다. 실제로 (이 경우와 같이) 컴파일러는 그렇지 않다고 가정 할 수 있습니다.코드를 더 빠르고 더 작게 만들 수 있다면 원하는대로 행동하십시오. 실행해서는 안되는 코드에서 발생하는 일은 누구나 추측 할 수 있습니다. 주변 코드 (컴파일러에 따라 컴파일러가 다른 코드를 생성 할 수 있음), 사용 된 변수 / 상수, 컴파일러 플래그 등에 따라 달라지며 컴파일러는 업데이트되고 동일한 코드를 다르게 작성할 수 있습니다. 코드 생성에 대한 다른 견해를 가진 다른 컴파일러를 얻으십시오. 또는 다른 기계를 얻으십시오. 동일한 아키텍처 라인의 다른 모델조차도 자체 정의되지 않은 동작을 가질 수 있습니다 (정의되지 않은 opcode를 찾으십시오, 일부 진취적인 프로그래머는 초기 기계 중 일부에서 때로는 유용한 일을한다는 것을 알았습니다 ...) . 없습니다 "컴파일러는 정의되지 않은 동작에 대해 명확한 동작을 제공합니다." 구현 정의 된 영역이 있으며 일관성있게 동작하는 컴파일러를 신뢰할 수 있어야합니다.


1
네, 정의되지 않은 행동이 무엇인지 잘 알고 있습니다. 그러나 특정 환경에서 언어의 특정 측면이 어떻게 구현되는지 알면 다른 유형이 아닌 특정 유형의 UB를 볼 수 있습니다. GCC는 정수 산술을 x86 정수 산술로 구현한다는 것을 알고 있습니다. 오버플로를 감 쌉니다. 그래서 나는 그런 행동을 가정했습니다. 내가 기대하지 않은 것은 bdonlan이 대답 한 것처럼 GCC가 다른 일을하는 것입니다.
Mysticial

7
잘못된. GCC는 정의되지 않은 동작을 호출하지 않는다고 가정 할 수 있으므로 발생하지 않는 것처럼 코드 를 내 보냅니다. 이 경우 않는 일, 지침은 당신이 가진 요청해야 할 일 없이 실행 얻을 정의되지 않은 행동, 결과는 CPU가하는 무엇이다. 즉, x86에서 x86 작업을 수행합니다. 다른 프로세서라면 완전히 다른 작업을 수행 할 수 있습니다. 또는 컴파일러는 정의되지 않은 동작을 호출하고 nethack을 시작한다는 것을 알기에 충분히 똑똑 할 수 있습니다 (예, 일부 고대 버전의 gcc는 정확히 그렇게했습니다).
vonbrand

4
나는 당신이 내 의견을 잘못 읽었다 고 생각합니다. 나는 "내가 예상하지 못한 것"이라고 말했다. 나는 GCC가 어떤 트릭을 끌어낼 것으로 기대 하지 않았다 .
Mysticial

1

컴파일러가 정수 오버플로를 "정의되지 않은"비정의 동작 (부록 L에 정의 된 형태)으로 간주하도록 지정하더라도 정수 오버플로의 결과는보다 구체적인 동작에 대한 특정 플랫폼 약속이없는 경우 최소한 "부분적으로 결정되지 않은 값"으로 간주됩니다. 이러한 규칙에 따라 1073741824 + 1073741824를 추가하면 임의로 2147483648 또는 -2147483648 또는 2147483648 mod 4294967296에 부합하는 다른 값을 산출하는 것으로 간주 될 수 있으며 추가로 얻은 값은 임의로 0 mod 4294967296에 부합하는 값으로 간주 될 수 있습니다.

오버플로가 "부분적으로 결정되지 않은 값"을 생성하도록 허용하는 규칙은 부록 L의 문자와 정신을 준수하기에 충분히 잘 정의되어 있지만 오버플로가 제한되지 않은 경우 컴파일러가 일반적으로 유용한 추론을하는 것을 막지는 않습니다. 정의되지 않은 행동. 컴파일러가 많은 경우 "최적화"를하는 것을 막을 수 있는데, 그 주요한 효과는 프로그래머가 이러한 "최적화"를 막는 유일한 목적을 가진 코드에 추가적인 혼란을 추가하도록 요구하는 것입니다. 그것이 좋은 것인지 아닌지는 자신의 관점에 달려 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.