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-optimizations
은 gcc 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
'program story' 카테고리의 다른 글
레일의 두 열에 고유 인덱스를 구현하는 방법 (0) | 2020.10.21 |
---|---|
공백이있는 JSON 개체 키에 액세스 (0) | 2020.10.21 |
문자열 + i를 연결하는 방법? (0) | 2020.10.21 |
페이지를 닫을 때 (떠나지 않을 때) jquery beforeunload? (0) | 2020.10.21 |
jQuery Button.click () 이벤트가 두 번 트리거됩니다. (0) | 2020.10.20 |