program story

C ++ 컴파일 버그?

inputbox 2020. 10. 21. 08:00
반응형

C ++ 컴파일 버그?


다음 코드가 있습니다.

#include <iostream>
#include <complex>
using namespace std;

int main() {
    complex<int> delta;
    complex<int> mc[4] = {0};

    for(int di = 0; di < 4; di++, delta = mc[di]) {
        cout << di << endl;
    }

    return 0;
}

"0, 1, 2, 3"을 출력하고 중지 할 것으로 예상하지만 "0, 1, 2, 3, 4, 5, ....."의 끝없는 시리즈를 출력합니다.

비교 di<4가 제대로 작동하지 않고 항상 true를 반환하는 것 같습니다.

그냥 주석 처리 ,delta=mc[di]하면 "0, 1, 2, 3"이 정상적으로 표시됩니다. 무고한 임무의 문제는 무엇입니까?

내가 사용하고 Ideone.com g ++ C ++ (14) -O2 옵션을 선택합니다.


이것은 정의되지 않은 동작으로 인해 mc루프의 마지막 반복에서 범위를 벗어난 배열에 액세스하고 있습니다. 일부 컴파일러는 정의되지 않은 동작이 없다는 가정을 중심으로 적극적인 루프 최적화를 수행 할 수 있습니다. 논리는 다음과 유사합니다.

  • mc범위를 벗어난 액세스 는 정의되지 않은 동작입니다.
  • 정의되지 않은 동작이 없다고 가정
  • 따라서 di < 4, 그렇지 않은 경우는 항상 true입니다 mc[di]정의되지 않은 동작을 호출 할 것이다

최적화가 켜져 있고 -fno-aggressive-loop-optimizations플래그를 사용하면 gcc가 무한 루프 동작이 사라집니다 ( 라이브 참조 ). 잠시 최적화하지만 -fno-공격적인 루프 - 최적화없이 라이브 예는 무한 루프 동작이 나타납니다 당신은 관찰한다.

코드godbolt 라이브 예제di < 4검사가 제거되고 무조건 jmp로 대체 되었음을 보여줍니다 .

jmp .L6

이는 GCC 4.8 이전 Breaks Broken SPEC 2006 Benchmarks에 설명 된 사례와 거의 동일합니다 . 이 기사에 대한 의견은 훌륭하고 읽을만한 가치가 있습니다. clang -fsanitize=undefined이이 사례를 재현 할 수 없지만 gcc를 사용 하는 기사에서 사례를 포착했음을 기록합니다 -fsanitize=undefined( 실시간 참조 ). 아마도 정의되지 않은 동작에 대한 추론을하는 옵티 마이저 주변에서 가장 악명 높은 버그는 Linux 커널 널 포인터 검사 제거 입니다.

이것은 공격적인 최적화이지만 C ++ 표준에서 정의되지 않은 동작은 다음과 같다고 명시하고 있습니다.

이 국제 표준이 요구 사항을 부과하지 않는 행동

어떤 본질적으로 무엇이든 가능하고 (노트 의미 강조 광산 ) :

[...] 허용되는 정의되지 않은 동작은 예측할 수없는 결과로 상황을 완전히 무시하는 것 , 번역 또는 프로그램 실행 중 환경 특성 (진단 메시지 발행 여부에 관계없이)의 문서화 된 방식으로 동작하는 것, 번역 종료 또는 실행 (진단 메시지 발행). [...]

gcc에서 경고를 받으려면 cout루프 외부 로 이동해야합니다. 그러면 다음 경고가 표시됩니다 ( live ).

warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
     for(di=0; di<4;di++,delta=mc[di]){ }
     ^

OP에 무슨 일이 일어나고 있는지 파악하기에 충분한 정보를 제공하기에 충분했을 것입니다. 이와 같은 불일치는 정의되지 않은 동작으로 볼 수있는 일반적인 동작 유형입니다. 정의되지 않은 동작에 대해 그러한 경고가 왜 부적절 할 수 있는지 더 잘 이해하기 위해 정의되지 않은 동작을 기반으로 최적화 할 때 왜 경고 할 수 없습니까? 좋은 읽기입니다.

참고 사항 -fno-aggressive-loop-optimizationsgcc 4.8 릴리스 노트에 설명되어 있습니다.


di인덱스를 사용하기 전에 증분하므로 mc루프를 통해 네 번째로 mc [4]를 참조하게되며 이는 배열의 끝을 지나서 문제가 될 수 있습니다.


di ++가 루프의 마지막 실행에서 실행되기 때문입니다.

예를 들면 다음과 같습니다.

int di = 0;
for(; di < 4; di++);
// after the loop di == 4
// (inside the loop we see 0,1,2,3)
// (inside the for statement, after di++, we see 1,2,3,4)

di == 4 일 때 mc []에 액세스하므로 범위를 벗어난 문제이며 잠재적으로 스택의 일부를 파괴하고 변수 di를 손상시킵니다.

해결책은 다음과 같습니다.

for(int di = 0; di < 4; di++) {
    cout << di << endl;
    delta = mc[di];
}

당신은 이것을 가지고 있습니다 :

for(int di=0; di<4; di++, delta=mc[di]) {
  cout<<di<<endl;
}

대신 이것을 시도하십시오.

for(int di=0; di<4; delta=mc[di++]) {
   cout<<di<<endl;
}

편집하다:

무슨 일이 일어나고 있는지 명확히하기 위해 For 루프의 반복을 분해 해보자 :

첫 번째 반복 : 처음에는 di가 0으로 설정됩니다. 비교 확인 : di가 4보다 작습니까? 네 괜찮아요. di를 1 씩 증가 시키십시오. 이제 di = 1. mc []의 "n 번째"요소를 잡고 델타로 설정하십시오. 이번에는 인덱스 된 값이 0이 아니라 1이기 때문에 두 번째 요소를 잡습니다. 마지막으로 for 루프 내에서 코드 블록을 수행합니다.

2nd iteration: Now di is set to 1. Comparison check: Is di less than 4? Yes and proceed. Increment di by 1. Now di = 2. Grab the "nth" element of mc[] and set it to be delta. This time we are grabbing the 3rd element since this indexed value is 2. Finally perform the code block/s inside the for loop.

3rd iteration: Now di is set to 2. Comparison check: Is di less than 4? Yes and proceed. Increment di by 1. Now di = 3. Grab the "nth" element of mc[] and set it to be delta. This time we are grabbing the 4th element since this indexed value is 3. Finally perform the code block/s inside the for loop.

4th iteration: Now di is set to 3. Comparison check: Is di less than 4? Yes and proceed. Increment di by 1. Now di = 4. (Can you see where this is going?) Grab the "nth" element of mc[] and set it to be delta. This time we are grabbing the 5th element since this indexed value is 4. Uh Oh we have a problem; our array size is only 4. Delta now has garbage and this is undefined behavior or corruption. Finally perform the code block/s inside the for loop using "garbage delta".

5th iteration. Now di is set to 4. Comparison check: Is di less than 4? No, break out of loop.

Corruption by exceeding bounds of contiguous memory (array).

참고URL : https://stackoverflow.com/questions/32506643/c-compilation-bug

반응형