2009-11-24 3 views
8

이 글을 쓰는 :조건부 연산자가 모든 인수를 평가해야합니까?

1: inline double f(double arg) { 
2: return arg == 0.0 ? 0.0 : 1./arg; 
3: } 
4: const double d = f(0.0); 

당신과 내가 사업부 0으로 결코가는 것을 분명히 알 수 있지만 2005 64 비트 컴파일러

line 4: warning C4723: potential divide by 0 

와 함께 제공되는 마이크로 소프트 비주얼 스튜디오 일어날 ...

아니면?

+11

평등에 대해 "이중"인수를 비교하는 데주의하십시오. 사악한 마법이 거기에서 발생합니다 ... – SadSido

+1

아니요. 완벽하게 정의 된 프로세스. 특히 '0.0 == -0.0'. 그러므로'1./arg'가 정의 된 모든 값 집합에 대해 우리는'arg! = 0.0'을 알고 있습니다. – MSalters

+1

@MSalters : 그러나 반올림 오류로 인해 arg는 0.0 (또는 그 문제에 대해 -0.0)이 될 수 있습니다. – jalf

답변

4

의심 할 여지없이 명백한 버그입니다.

경고의 의도는 프로그램의 모든 부서에 대해 경고하지 않기위한 것입니다. 그것은 합리적인 프로그램에서 너무 시끄러운 것입니다. 대신, 당신이 인수를 확인해야 할 때 경고 할 의도가 있습니다. 이 경우 인수를 확인했습니다. 따라서 컴파일러는이를 지적하고 종료해야합니다.

이러한 기능의 기술적 인 구현은 코드 분기의 변수에 특정 속성을 지정하여 수행됩니다. 가장 일반적인 속성 중 하나는 3 상태 "Is null"입니다. 지점 이전에 arg은 외부 변수이고 arg [[Isnull]]은 알 수 없습니다. 그러나 arg에 대한 점검 후에 두 가지가 있습니다. 첫 번째 지점에서 arg [[Isnull]]이 true입니다. 두 번째 지점에서 arg [[Isnull]]은 거짓입니다.

이제 0으로 나누기 및 null 포인터 경고를 생성 할 때는 [[IsNull] 특성을 확인해야합니다. 사실이라면 심각한 경고/오류가 있습니다. 알 수없는 경우 컴파일러가 증명할 수있는 것 이상의 잠재적 인 문제점 인 위에 표시된 경고를 생성해야합니다. 그러나이 경우 [[isNull]] 특성은 False입니다. 인간과 동일한 형식 논리에 의한 컴파일러는 위험이 없음을 알고 있습니다.

하지만 컴파일러에서 내부적으로 [[Isnull]] 속성을 사용한다는 것을 어떻게 알 수 있습니까? 첫 단락을 상기 해보십시오 : 그것 없이는 항상 또는 결코 경고해야합니다. 때로는 경고 메시지가 표시되며 [[IsNull]] 속성이 있어야합니다.

+0

견고한 추론입니다. 나는 그것과 함께 살 수 있습니다 - 특히 그것이 내 직감을 증명해주기 때문에. – xtofl

+0

최근의 Linux 버그가이 속성으로 인해 발생했습니다. 문제가되는 코드에서 포인터는 먼저 참조를 해제하고 (기본적으로'int * member = & (foo-> bar);')'if (! foo) return;'를 체크했다. 그러나 참조가 없기 때문에,'foo [ [IsNull]]'속성은 이미 false로 설정되었고 NULL 검사가 최적화되었습니다. – MSalters

11

컴파일러는 모든 코드 경로를 정적으로 분석하고 모든 가능성을 항상 고려할 수 없습니다. 이론적으로 소스 코드를 살펴봄으로써 프로그램 행위를 완벽하게 분석하는 것은 문제를 해결할 수있는 해결책을 제공 할 수 있습니다. 컴파일러에는 규칙을 탐지하기위한 정적 분석 규칙 세트가 있습니다. C++ 표준에서는 컴파일러가 이러한 종류의 경고를 발생하도록 요구하지 않으므로 안됩니다. 그것은 버그가 아닙니다. 존재하지 않는 기능과 같습니다.

+0

맞아. 그것은 경고와 관련되기 때문에 결코 C++ 표준 관점에서 버그가 될 수 없습니다. 그러나이 '결함'으로 인해 '컴퓨터 프로그램'관점에서 완벽하게 유효한 코드를 다시 작성해야합니다 (또는 '#pragma's로 둘러 쌉니다). 버그입니다. – xtofl

+0

@xtofl : 경고의 핵심 단어는 "잠재력"입니다. –

+0

동의하지만, 핵심적인 고통은 경고를 오류로 처리하려고 시도하고 '거짓 긍정'경고로 그렇게 할 수 없다는 것입니다. – xtofl

7

아니요, 아니요, 조건부 연산자는 두 인수를 모두 계산하지 않습니다. 그러나 컴파일러가 그러한 것을 탐지 할 수있는 경우 잠재적 인 0으로 나누기가 일반적으로보고됩니다. 이 연산자의 동작을 설명하기 위해 표준이 ~ 2 페이지를 차지한다는 것은 아무런 이유도 아닙니다.

N-4411에서

:

5.16 조건 연산자

1 조건식 기 오른쪽에서 왼쪽. 첫 번째 표현식은 문맥 상 부울로 변환 된 (조항 4)입니다. 평가되고 참인 경우 조건부 결과는 이고 두 번째는 표현의 값이고 그렇지 않으면 세 번째 표현식의 값입니다. 두 번째 및 세 번째 식 중 하나만이 입니다. 첫 번째 표현식과 관련된 모든 값 계산 및 부작용은 값 계산과 부작용 두 번째 또는 세 번째 표현식과 관련된 모든 값 앞에 계산됩니다. 또한

, 주 : 두 번째 및 세 번째 피연산자 다른 타입을 가지고, 그렇지 않으면

3, 하나 (아마도 CV 수식) 클래스 타입 시도가되어왔다 으로 각각의 피연산자를 유형으로 변환합니다.

인용 한 예는 두 번째 및 세 번째 표현 모두에 대해 동일한 유형입니다. 안심하십시오. 첫 번째 만 평가됩니다.

+0

나는 포인트 3이 실제로 두 피연산자가 모두 평가된다는 것을 의심한다. 이 문제는'std :: string s ("A"); const char * c = "B"; std :: string a = cond()? s : c; const char * b = cond()? s : c;'컴파일해야 할 것인가 또는하지 않을 것인가 (연산자의 결과 타입이 무엇인지 결정해야한다. 컴파일러는 무엇에 캐스트 될 수 있는지 (이 경우 첫 번째?) :'const char *'가 암시 적으로 변환 될 수 있기 때문에 컴파일한다. std :: string으로하지만 연산자의 결과 타입이'std :: string'이므로 암시 적으로'const char *'로 변환 될 수 없으므로 두번째 컴파일은 수행되지 않습니다. – UncleBens

+0

Accpeted and edited. 나는 그것을 지정해야만했다. – dirkgently

3

나누기 코드가 생성되므로 경고가 표시됩니다. 그러나 arg이 0 일 때 분기가 수행되지 않으므로 안전합니다.

3

연산자 == 부동 소수점 숫자가 안전하지 않습니다 (반올림 문제로 인해 신뢰할 수 없음). 이 특별한 경우에는 실제로 안전하기 때문에 경고를 무시할 수 있지만 컴파일러는 일반적인 경우에 예측할 수없는 연산자를 기반으로 한 분석을하지 않습니다.

3

조건부 연산자는 모든 인수를 평가해서는 안됩니다. 하지만 arg은 0과 거의 같을 수 있으므로 arg == 0.0false이되지만 1./arg은 "0으로 나누기"결과를 제공합니다. 그래서 여기서 경고가 유용하다고 생각합니다.

그런데 Visual C++ 2008에서는 이러한 경고를 제공하지 않습니다.

+0

그건 의미가 있습니다. 감사. – xtofl

+1

C의 IEEE 754 부동 소수점 나누기는 0으로 나누기를 실행하지 않습니다. 0으로 나눈 결과는 + Infinity, -Infinity 또는 NaN입니다. –

+0

결과는 비정규 숫자에 대해 + Infinity 또는 -Infinity가 될 수 있지만 0으로 나누는 것이 아니라 오버플로라고합니다. – starblue

0

다른 주석과 함께 : 경고는 컴파일러에서 생성되며 나중에 실행되는 최적화 프로그램에 의해 제거됩니다. 링크 단계에서도 가능합니다.

아니요, 아니요, 버그가 아닙니다. 이 경고는 컴파일러에서 제공하는 추가 서비스로 표준에 의해 요구되지는 않습니다. 이것은 컴파일러/링커 아키텍처의 불행한 부작용입니다.

0

Microsoft 특정 __assume 키워드를 사용하여 경고를 피할 수 있습니다. 조건부 연산자로 묶을 수 있는지 확실하지 않습니다. 그렇지 않으면

if (arg == 0.0){ 
    return 0.0; 
} 
else { 
__assume(arg != 0.0); 
    return 1./arg; 
} 

과 같은 것이 좋습니다. 물론,이 기능을 수행하는 동안 적절한 경고 메시지 인 #pragma을 사용하여 경고를 중지하십시오.

관련 문제