온라인 IDE에서 이상하게 작동하는 프로그램


82

아래 C ++ 프로그램 ( 소스 )을 보았습니다 .

#include <iostream>
int main()
{
    for (int i = 0; i < 300; i++)
        std::cout << i << " " << i * 12345678 << std::endl;
}

간단한 프로그램처럼 보이며 로컬 컴퓨터에 다음과 같은 올바른 출력을 제공합니다.

0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574

그러나 codechef 와 같은 온라인 IDE 에서는 다음과 같은 출력을 제공합니다.

0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597

for루프가 300에서 종료 되지 않는 이유는 무엇 입니까? 또한이 프로그램은 항상에서 종료됩니다 4169. 왜 4169다른 가치가 아닌가?


45
아마도 부호있는 정수 오버플로에 정의되지 않은 동작이 있기 때문일 것입니다.
eerorika

17
서명 된 오버플로가 UB라는 것을 알고 있지만 이것은 놀라운 실패입니다 ...
Galik

45
UB가 제한되어 있다고 가정하지 않는 좋은 교훈
MM

12
왜 첫 번째 출력이 "올바른 출력"이라고 생각하는지 궁금합니다.
궤도에서 가벼운 경주

5
@LightnessRacesinOrbit-글쎄, 그 알림 가치가 있습니다!
davidbak

답변:


108

온라인 컴파일러는 GCC 또는 호환 컴파일러를 사용한다고 가정하겠습니다. 물론 다른 컴파일러도 동일한 최적화를 수행 할 수 있지만 GCC 문서에서는 수행하는 작업을 잘 설명합니다.

-faggressive-loop-optimizations

이 옵션은 루프 최적화 프로그램에 언어 제약 조건을 사용하여 루프 반복 횟수에 대한 경계를 파생하도록 지시합니다. 예를 들어 부호있는 정수 오버플로 또는 범위를 벗어난 배열 액세스를 유발하여 루프 코드가 정의되지 않은 동작을 호출하지 않는다고 가정합니다. 루프 반복 횟수에 대한 경계는 루프 풀기 및 필링 및 루프 종료 테스트 최적화를 안내하는 데 사용됩니다. 이 옵션은 기본적으로 활성화되어 있습니다.

이 옵션은 단지 UB가 입증 된 경우를 기반으로 가정을 허용합니다. 이러한 가정을 활용하려면 상수 접기와 같은 다른 최적화를 활성화해야 할 수 있습니다.


부호있는 정수 오버플로에 정의되지 않은 동작이 있습니다. 옵티마이 저는 i173보다 큰 값이 UB를 유발 한다는 것을 증명할 수 있었고 , UB가 없다고 가정 할 수 있기 때문에 173보다 크지 않다고 가정 할 수도 i있습니다. 그런 다음 그것이 i < 300항상 참 임을 추가로 증명할 수 있습니다. 루프 조건을 최적화 할 수 있습니다.

왜 4169이고 다른 가치가 아닌가?

이러한 사이트는 표시되는 출력 줄 (또는 문자 또는 바이트) 수를 제한하고 동일한 제한을 공유합니다.


3
컴파일러 i가 173보다 큰 값에 대해 UB가 있음을 증명할 수 있다면 무의미한 최적화를 수행하는 대신 경고를 표시하지 않는 이유는 무엇입니까?
Jabberwocky

7
@MichaelWalz 그것은 하나를 방출합니다.
HolyBlackCat

3
@MichaelWalz : 중복 된 부울 테스트를 제거하는 것은 무의미한 최적화가 아닙니다. 매우 유용합니다. 무엇을 수 (대부분) 무의미하지 않도록 추가 코드를 추가 / 깨진 소스 코드의 존재의 최적화를 취소.

25
@MichaelWalz : 당신이 제안으로 (실제로 그것을 때때로하는 가능성 발생에 경고 할 수 있지만 컴파일러는 안정적 UB를 감지 할 수 않습니다 여기). 대신 UB가 없을 것이라는 가정하에 최선의 의도로 진행할 수 있습니다 . 그것들은 아마도 미묘하지만 실제로는 실질적으로 다른 것입니다. 컴파일러가 "아하! UB! 이제 그런 최적화를 켤 수있다"는 것과는 다릅니다. 최적화는 항상 거기에있었습니다. 항상 이와 같은 일을 하고 있습니다 . 그러나 프로그램이 올바르게 작성되는 한 예상 의미 체계가 변경되지 않습니다.
궤도

20
비유로, 집의 현관 제조업체는 금속 조각이 전략적으로 중간 어딘가에 배치되어 있으면 더 견고하다고 결정할 수 있습니다. 문에 구멍을 뚫어 문을 잘못 사용 하지 않는 한 절대 눈치 채지 못할 것 입니다.
궤도

40

"정의되지 않은 동작은 정의되지 않았습니다." (씨)

codechef에서 사용되는 컴파일러는 다음 논리를 사용하는 것 같습니다.

  1. 정의되지 않은 동작은 발생할 수 없습니다.
  2. i * 12345678오버플로되고 UB if i > 173(32 비트라고 가정 int)가됩니다.
  3. 따라서, i초과 할 수 없다 173.
  4. 따라서 i < 300불필요하며 true.

루프 자체는 무한한 것처럼 보입니다. 분명히 codechef는 특정 시간이 지나면 프로그램을 중지하거나 출력을 자릅니다.


11
@ArpanMangal 방금 출력의 문자 수를 계산했는데 2^16. 분명히 그것은 둘 다 출력을 2^16문자로 자르는 우연의 일치입니다 .
HolyBlackCat

3
@ArpanMangal : 4169와 같은 비강 악마이며 현재 ideone과 codechef에서 파티를하고 있습니다. UB는 정의되지 않았으며 비강 악마를 포함하여 무엇이든 될 수 있습니다. 진지하게 UB를 분석하려는 것은 시간 낭비입니다. 그 시간을 사용하여 어떻게 발생하지 않도록 할 수 있는지 알아 내십시오.
jmoreno

6
@jmoreno 컴파일러 디자인에 관심이 있다면 시간 낭비가 아닙니다
user253751

2
@jmoreno하지만 이번에는 분석이 가능했습니다. UB가 정확히 어떻게 작업을 중단하는지 이해하면 UB가 허용되는 경우 결론을 내리는 데 유용 할 수 있습니다.
HolyBlackCat

4
@jmoreno 우주가하는 일은 (정의상) 옳습니다. 그렇다면 천문학 연구의 요점은 무엇입니까?
user253751

11

최대 값이 아직 정의되지 않은 동작으로 평가 되기 때문에 루프 내부의 174 번째 반복에서 정의되지 않은 동작을 호출 하고 있습니다. 따라서 특히 귀하의 경우에 최적화 플래그가 설정된 GCC 컴파일러에서 UB의 효과를 관찰하고 있습니다. 컴파일러가 다음 경고를 발행하여 이에 대해 경고했을 가능성이 있습니다.forint2147483647174 * 1234567892148147972

warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]

-O2다른 결과를 관찰 하려면 ( ) 최적화 플래그를 제거하십시오 .


4
정의되지 않은 동작이 소급 효과를 가질 수 있다는 점은 주목할 가치가 있습니다. UB는 반복 174에서 발생하기 때문에 표준은 루프의 처음 173 회 반복이 예상대로 진행되어야한다고 요구하지도 않습니다!

@Hurkyl 과연. UB가 이전 진술을 포함하여 전체 프로그램에 UB를 표시하는 것은 다소 역설적입니다.
Ron

12
@ 론 : 역설적이지 않습니다. 프로그램에 잘 정의 된 의미가 있거나 그렇지 않습니다. 기간. C ++ 코드는 수행 할 일련의 명령이 아닙니다. 그것에는 프로그램의 설명 .
궤도에서 가벼운 경주

@LightnessRacesinOrbit : 프로그램이 부분적으로 지정되지 않았지만 완전히 정의되지 않은 의미를 가질 수 있습니다. C 표준은이 개념을 "Bounded UB"라고 부르는 것에 적용하려고 시도하지만, 사용하는 언어는 약간 모호합니다. 컴파일러가 정수 오버플로와 같은 것을 처리 할 때 광범위하지만 무제한이 아닌 자유를 허용하는 것은 다른 유용한 최적화를 방해하지 않지만 신뢰할 수없는 소스에서받은 데이터를 처리하는 데 언어를 훨씬 더 적합하게 만듭니다.
supercat

@supercat : 실제로, 지정되지 않은 동작은 정의되지 않은 동작과 동일하지 않습니다. 우리는 후자에 대해 논의하고 있습니다
궤도의 Lightness Races

7

컴파일러는 정의되지 않은 문제가 발생하지 않을 것이라고 가정 할 수 있으며, 서명 오버 플로우가 UB 때문에, 결코 있다고 가정 할 수 있습니다 i * 12345678 > INT_MAX, 따라서도 i <= INT_MAX / 12345678 < 300따라서 체크를 해제합니다 i < 300.

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