컴파일러가 데드 코드 감지를 완전히 해결할 수없는 이유는 무엇입니까?


192

C 또는 Java에서 사용한 컴파일러는 데드 코드 방지 기능이 있습니다 (라인이 실행되지 않을 때 경고). 교수님은 컴파일러가이 문제를 완전히 해결할 수는 없다고 말합니다. 왜 그런지 궁금했습니다. 이론 기반 클래스이므로 컴파일러의 실제 코딩에 너무 익숙하지 않습니다. 그러나 나는 그들이 무엇을 점검하는지 (예 : 가능한 입력 문자열 대 허용되는 입력 등) 궁금한 이유가 무엇인지 궁금했습니다.


91
루프, 그 이후에 넣어 코드를 확인 후 적용 en.wikipedia.org/wiki/Halting_problem
zapl

48
if (isPrime(1234234234332232323423)){callSomething();}이 코드는 어떤 것을 호출합니까? 함수가 호출되는 것을 결정하는 것이 프로그램에 포함시키는 것보다 훨씬 비싸다는 다른 많은 예가 있습니다.
idclev 463035818

33
public static void main(String[] args) {int counterexample = findCollatzConjectureCounterexample(); System.out.println(counterexample);}<-println 호출 데드 코드입니까? 인간조차도 그 문제를 해결할 수 없습니다!
user253751

15
@ tobi303은 좋은 예가 아닙니다. 소수를 인수 분해하는 것은 정말 쉽습니다. 상대적으로 효율적으로 인수 분해하지는 않습니다. 중지 문제는 NP에 없으며 해결할 수 없습니다.
en_Knight

57
@alephzero와 en_Knight-둘 다 틀렸다. isPrime이 좋은 예입니다. 함수가 소수를 확인한다고 가정했습니다. 해당 번호는 일련 번호 일 수 있으며 사용자가 Amazon Prime 멤버인지 확인하기 위해 데이터베이스 조회를 수행합니까? 좋은 예는 조건이 상수인지 아닌지를 아는 유일한 방법은 실제로 isPrime 함수를 실행하기 때문입니다. 따라서 이제는 컴파일러도 통역사가되어야합니다. 그러나 여전히 데이터가 일시적인 경우에는 해결되지 않습니다.
Dunk

답변:


275

데드 코드 문제는 Halting 문제 와 관련이 있습니다 .

Alan Turing은 프로그램에 제공 될 일반 알고리즘을 작성하는 것이 불가능하며 해당 프로그램이 모든 입력에 대해 중지되는지 여부를 결정할 수 있음을 증명했습니다. 특정 유형의 프로그램에 대해서는 이러한 알고리즘을 작성할 수 있지만 모든 프로그램에 대해 해당되는 것은 아닙니다.

이것이 죽은 코드와 어떤 관련이 있습니까?

앞뒤가 맞지 않는 문제는 환원 죽은 코드를 찾는 문제이다. 즉, 모든 프로그램 에서 데드 코드를 감지 할 수있는 알고리즘을 찾으면 해당 알고리즘을 사용 하여 프로그램이 중지 되는지 테스트 할 수 있습니다 . 이것이 불가능하다는 것이 입증되었으므로 데드 코드에 대한 알고리즘 작성도 불가능합니다.

죽은 코드에 대한 알고리즘을 Halting 문제에 대한 알고리즘으로 어떻게 전송합니까?

간단 함 : 중지하려는 프로그램이 끝난 후 코드 줄을 추가합니다. 데드 코드 감지기가이 라인이 죽은 것을 감지하면 프로그램이 중지되지 않는다는 것을 알 수 있습니다. 그렇지 않은 경우 프로그램이 중지됨을 알 수 있습니다 (마지막 줄로 이동 한 다음 추가 된 코드 줄로 이동).


컴파일러는 일반적으로 컴파일 타임에 죽었 음을 입증 할 수있는 것들을 확인합니다. 예를 들어, 컴파일 타임에 거짓으로 판단 될 수있는 조건에 의존하는 블록. 또는 return(같은 범위 내에서) 뒤에 나오는 진술 .

이들은 특정한 경우이므로 알고리즘을 작성할 수 있습니다. 조건이 구문 상 모순인지 여부를 확인하여 항상 거짓을 반환하는 알고리즘과 같이 더 복잡한 경우에 대한 알고리즘을 작성할 수는 있지만 모든 가능한 경우를 다루지는 않습니다.


8
여기서는 정지 문제가 적용되지 않는다고 주장합니다. 실제 세계의 모든 컴파일러의 컴파일 대상 인 모든 플랫폼은 액세스 할 수있는 최대 데이터 양을 가지고 있으므로 최대 상태 수를 의미합니다. 실제로 튜링 머신이 아닌 유한 상태 머신입니다. 정지 문제는 FSM에서 해결할 수 없으므로 실제 컴파일러는 데드 코드 감지를 수행 할 수 있습니다.
Vality

50
@Vality 64 비트 프로세서는 2 ^ 64 바이트를 처리 할 수 ​​있습니다. 모든 256 ^ (2 ^ 64) 상태를 검색하여 재미있게 보내십시오!
Daniel Wagner

82
@DanielWagner 이것은 문제가되지 않습니다. 256^(2^64)상태 검색 은입니다 O(1). 따라서 다항식 시간에 데드 코드 감지를 수행 할 수 있습니다.
아기들

13
@Leliel, 그건 냉소적이었습니다.
폴 드레이퍼

44
@Vality : 대부분의 최신 컴퓨터에는 디스크, 입력 장치, 네트워크 통신 등이 있습니다. 완전한 분석에는 문자 그대로 인터넷과 연결된 모든 장치를 포함한 모든 장치를 고려해야합니다. 다루기 힘든 문제는 아닙니다.
Nat

77

자, 정지 문제의 결정 불가능에 대한 고전적인 증거를 가지고 정지 검출기를 데드 코드 검출기로 변경합시다!

C # 프로그램

using System;
using YourVendor.Compiler;

class Program
{
    static void Main(string[] args)
    {
        string quine_text = @"using System;
using YourVendor.Compiler;

class Program
{{
    static void Main(string[] args)
    {{
        string quine_text = @{0}{1}{0};
        quine_text = string.Format(quine_text, (char)34, quine_text);

        if (YourVendor.Compiler.HasDeadCode(quine_text))
        {{
            System.Console.WriteLine({0}Dead code!{0});
        }}
    }}
}}";
        quine_text = string.Format(quine_text, (char)34, quine_text);

        if (YourVendor.Compiler.HasDeadCode(quine_text))
        {
            System.Console.WriteLine("Dead code!");
        }
    }
}

YourVendor.Compiler.HasDeadCode(quine_text)반환 false하면 라인 System.Console.WriteLn("Dead code!");이 실행되지 않으므로이 프로그램에는 실제로 데드 코드 있으며 검출기가 잘못되었습니다.

그러나이 값을 반환 true하면 라인 System.Console.WriteLn("Dead code!");이 실행되고 프로그램에 더 이상 코드가 없으므로 데드 코드가 없으므로 검출기가 잘못되었습니다.

따라서 "데드 코드가 있습니다"또는 "데드 코드가 없습니다"만 반환하는 데드 코드 감지기가 있습니다.


1
내가 당신의 주장을 올바르게 이해했다면 기술적으로 다른 옵션은 데드 코드 검출기 인 것을 작성할 수는 없지만 일반적인 경우 데드 코드 검출기를 작성할 수 있다는 것입니다. :-)
abligh

1
Godelian 응답의 증가.
Jared Smith

@abligh 어, 그건 나쁜 단어 선택이었습니다. 실제로 데드 코드 검출기의 소스 코드를 자체적으로 공급하는 것이 아니라 그것을 사용하는 프로그램의 소스 코드입니다. 분명히 어떤 시점에서는 아마도 자체 코드를 살펴 봐야하지만 비즈니스 일 것입니다.
Joker_vD

65

중지 문제가 너무 모호한 경우이 방법으로 생각하십시오.

모든 양의 정수 n에 대해 참으로 여겨지 지만 모든 n에 대해 참으로 입증되지는 않은 수학적 문제를 취하십시오 . 좋은 예는 Goldbach의 추측 인데, 2보다 큰 양의 정수는 두 소수의 합으로 표현 될 수 있습니다. 그런 다음 (적절한 bigint 라이브러리를 사용하여)이 프로그램을 실행하십시오 (의사 코드는 다음과 같습니다).

 for (BigInt n = 4; ; n+=2) {
     if (!isGoldbachsConjectureTrueFor(n)) {
         print("Conjecture is false for at least one value of n\n");
         exit(0);
     }
 }

구현은 isGoldbachsConjectureTrueFor()독자에게는 연습으로 남겨 두지 만이 목적을 위해 모든 소수에 대한 간단한 반복이 될 수 있습니다.n

논리적으로 위의 내용은 다음과 같아야합니다.

 for (; ;) {
 }

(즉 무한 루프) 또는

print("Conjecture is false for at least one value of n\n");

골드 바흐의 추측은 사실이거나 사실이 아니어야합니다. 컴파일러가 항상 데드 코드를 제거 할 수 있다면 어느 경우 든 여기서 제거 할 수있는 데드 코드가있을 것입니다. 그러나 최소한 그렇게하면 컴파일러는 임의로 어려운 문제를 해결해야합니다. 우리는 문제를 제공 할 수 라도 유용 열심히 코드의 비트를 제거하는 결정하기 위해 (예를 들어, NP 완전 문제를) 해결해야 할 것이다. 예를 들어 우리가이 프로그램을한다면 :

 String target = "f3c5ac5a63d50099f3b5147cabbbd81e89211513a92e3dcd2565d8c7d302ba9c";
 for (BigInt n = 0; n < 2**2048; n++) {
     String s = n.toString();
     if (sha256(s).equals(target)) {
         print("Found SHA value\n");
         exit(0);
     }
 }
 print("Not found SHA value\n");

우리는 프로그램이 "발견 된 SHA 값"또는 "발견되지 않은 SHA 값"을 출력한다는 것을 알고 있습니다 (어느 것이 참인지 알려면 보너스 포인트). 그러나 컴파일러가 2 ^ 2048 반복의 순서를 취하는 합리적으로 최적화 할 수 있습니다. 실제로 위의 프로그램이 최적화없이 아무것도 인쇄하지 않고 우주의 열사병까지 실행될 것이라고 예측 했으므로 실제로는 큰 최적화가 될 것입니다.


4
그것은 +1까지의 가장 좋은 답변입니다
jean

2
특히 흥미로운 점은 C 표준이 루프가 종료된다고 가정 할 때 허용하거나 허용하지 않는 것에 대한 모호성입니다. 컴파일러가 결과가 실제로 필요할 때까지 결과가 사용되거나 사용되지 않을 수있는 느린 계산을 지연시킬 수있는 가치가 있습니다. 컴파일러가 계산 종료를 증명할 수없는 경우에도이 최적화가 유용 할 수 있습니다.
supercat

2
2 ^ 2048 반복? 깊은 생각 조차도 포기할 것입니다.
Peter Mortensen

대상이 64 진수 16 진수의 임의 문자열 인 경우에도 "확률 된 SHA 값"을 매우 높은 확률로 인쇄합니다. 하지 않는 한 sha256반환 바이트 배열 및 바이트 배열은 해당 언어로 문자열과 동일 비교하지 않습니다.
user253751

4
Implementation of isGoldbachsConjectureTrueFor() is left as an exercise for the reader이것은 나를 괴롭혔다.
biziclop

34

C ++ 또는 Java에 Eval유형 함수 가 있는지는 모르지만 많은 언어 에서 name으로 메소드 호출 할 수 있습니다 . 다음 (고려 된) VBA 예를 고려하십시오.

Dim methodName As String

If foo Then
    methodName = "Bar"
Else
    methodName = "Qux"
End If

Application.Run(methodName)

호출 할 메소드의 이름은 런타임까지 알 수 없습니다. 따라서 정의에 따라 컴파일러는 특정 메소드가 호출되지 않는다는 것을 절대 확실하게 알 수 없습니다.

실제로 이름으로 메서드를 호출하는 예를 보면 분기 논리가 필요하지 않습니다. 간단히 말해서

Application.Run("Bar")

컴파일러가 결정할 수있는 것 이상입니다. 코드가 컴파일 될 때 모든 컴파일러는 특정 문자열 값이 해당 메소드에 전달된다는 것을 알고 있습니다. 런타임까지 해당 메소드가 존재하는지 확인하지 않습니다. 더 일반적인 방법을 통해 다른 곳에서 메서드를 호출하지 않으면 죽은 메서드를 찾으려고 시도하면 오 탐지를 반환 할 수 있습니다. 리플렉션을 통해 코드를 호출 할 수있는 모든 언어에 동일한 문제가 있습니다.


2
Java (또는 C #)에서는 리플렉션을 사용하여 수행 할 수 있습니다. C ++ 매크로를 사용하여 불쾌감을 줄 수 있습니다. 예쁘지는 않지만 C ++은 거의 없습니다.
대럴 호프만

6
@DarrelHoffman-코드가 컴파일러에 제공되기 전에 매크로가 확장되므로 매크로는 그렇게하는 것이 아닙니다. 함수에 대한 포인터는이 작업을 수행하는 방법입니다. 몇 년 동안 C ++을 사용하지 않았으므로 정확한 유형 이름이 잘못된 경우 실례하지만 함수 포인터에 문자열 맵을 저장할 수 있습니다. 그런 다음 사용자 입력에서 문자열을 받아들이고 맵에서 해당 문자열을 찾은 다음 지정된 함수를 실행하는 무언가를 갖습니다.
ArtOfWarfare

1
@ArtOfWarfare 우리는 그것이 어떻게 이루어질 수 있는지에 대해 이야기하고 있지 않습니다. 분명히이 상황을 찾기 위해 코드의 의미 론적 분석을 수행 할 수 있으며, 요점은 컴파일러 가 그렇지 않다는 것 입니다. 아마도 가능할 수도 있지만 그렇지 않을 수도 있습니다.
RubberDuck

3
@ArtOfWarfare : 당신이 nitpick하고 싶다면, 물론. 전처리 기는 컴파일러의 일부라고 생각하지만 기술적으로 그렇지 않다는 것을 알고 있습니다. 어쨌든 함수 포인터는 함수가 어디에서나 직접 참조되지 않는다는 규칙을 어길 수 있습니다. C #의 대리자와 마찬가지로 직접 호출 대신 포인터와 같습니다. C ++은 일반적으로 컴파일러가 작업을 간접적으로 수행하는 방법이 너무 많기 때문에 예측하기가 훨씬 어렵습니다. "모든 참조 찾기"처럼 ​​단순한 작업조차도 typedef, 매크로 등에 숨길 수 있으므로 사소한 것이 아닙니다. 데드 코드를 쉽게 찾을 수 없다는 것은 놀라운 일이 아닙니다.
Darrel Hoffman

1
이 문제에 직면하기 위해 동적 메소드 호출이 필요하지 않습니다. Java 또는 C #의 이미 컴파일 된 클래스 또는 동적 링크 메커니즘을 갖춘 다른 컴파일 된 언어에 의존하는 아직 작성되지 않은 함수로 모든 공용 메소드를 호출 할 수 있습니다. 컴파일러가 이들을 "데드 코드"로 제거하면 배포를 위해 사전 컴파일 된 라이브러리 (NuGet, jar, 이진 컴포넌트가 포함 된 Python 휠)를 패키지 할 수 없습니다.
jpmc26

12

고급 컴파일러는 무조건 데드 코드를 감지하고 제거 할 수 있습니다.

그러나 조건부 데드 코드도 있습니다. 컴파일시 알 수 없으며 런타임 중에 만 감지 할 수있는 코드입니다. 예를 들어, 소프트웨어는 사용자 선호도에 따라 특정 기능을 포함하거나 제외하도록 구성하여 특정 시나리오에서 특정 코드 섹션을 죽인 것처럼 보일 수 있습니다. 그것은 실제 죽은 코드가 아닙니다.

테스트를 수행하고, 종속성을 해결하고, 조건부 데드 코드를 제거하고, 효율성을 위해 런타임에 유용한 코드를 재결합 할 수있는 특정 도구가 있습니다. 이것을 동적 데드 코드 제거라고합니다. 그러나 보시다시피 컴파일러의 범위를 벗어납니다.


5
"고급 컴파일러가 무조건 데드 코드를 감지하고 제거 할 수 있습니다." 가능성이없는 것 같습니다. 코드 사망은 주어진 함수의 결과에 따라 달라질 수 있으며 해당 함수는 임의의 문제를 해결할 수 있습니다. 따라서 귀하의 진술은 고급 컴파일러가 임의의 문제를 해결할 수 있다고 주장합니다.
Taemyr

6
@Taemyr 그렇다면 무조건 죽었다고 알려져 있지 않습니까?
JAB

1
@Taemyr "무조건"이라는 단어를 오해하는 것 같습니다. 코드 데드가 함수의 결과에 의존하는 경우 조건부 데드 코드입니다. "조건"은 기능의 결과입니다. "무조건적"이 되려면 결과에 의존 하지 않아도됩니다 .
Kyeotic

12

간단한 예 :

int readValueFromPort(const unsigned int portNum);

int x = readValueFromPort(0x100); // just an example, nothing meaningful
if (x < 2)
{
    std::cout << "Hey! X < 2" << std::endl;
}
else
{
    std::cout << "X is too big!" << std::endl;
}

이제 포트 0x100이 0 또는 1 만 반환하도록 설계되었다고 가정합니다.이 경우 컴파일러는 else블록이 절대 실행되지 않는다는 것을 알 수 없습니다 .

그러나이 기본 예에서 :

bool boolVal = /*anything boolean*/;

if (boolVal)
{
  // Do A
}
else if (!boolVal)
{
  // Do B
}
else
{
  // Do C
}

여기서 컴파일러는 else 블록이 죽은 코드임을 . 따라서 컴파일러는 데드 코드를 파악하기에 충분한 데이터가있는 경우에만 데드 코드에 대해 경고 할 수 있으며 주어진 블록이 데드 코드인지 알아 내기 위해 해당 데이터를 적용하는 방법을 알아야합니다.

편집하다

컴파일 타임에 데이터를 사용할 수없는 경우도 있습니다.

// File a.cpp
bool boolMethod();

bool boolVal = boolMethod();

if (boolVal)
{
  // Do A
}
else
{
  // Do B
}

//............
// File b.cpp
bool boolMethod()
{
    return true;
}

a.cpp를 컴파일하는 동안 컴파일러는 boolMethod항상을 반환 한다는 것을 알 수 없습니다 true.


1
컴파일러 가 알지 못한다는 사실은 엄격하지만 , 링커 가 알 수 있는지 여부를 묻는 것이 문제의 정신에 있다고 생각합니다 .
Casey Kuball

1
@Darthfett 그것은 링커 의 책임 이 아닙니다 . 링커는 컴파일 된 코드의 내용을 분석하지 않습니다. 링커 (일반적으로 말하면)는 메서드와 전역 데이터를 연결하기 만하면 내용에 신경 쓰지 않습니다. 그러나 일부 컴파일러에는 소스 파일 (ICC와 같은)을 연결 한 다음 최적화를 수행 할 수있는 옵션이 있습니다. 이 경우 EDIT 아래의 경우 는 다루지 만이 옵션은 특히 프로젝트가 큰 경우 컴파일 시간에 영향을줍니다.
Alex Lop.

이 대답은 나에게 오해의 소지가 있습니다. 모든 정보를 이용할 수있는 것은 아니기 때문에 불가능한 두 가지 예를 제시하고 있지만 정보가 있어도 불가능하다고 말해서는 안됩니까?
Anton Golov

@AntonGolovIt는 항상 사실이 아닙니다. 대부분의 경우 정보가있는 경우 컴파일러는 데드 코드를 감지하여 최적화 할 수 있습니다.
Alex Lop.

@abforce는 코드 블록입니다. 다른 것이었을 수도 있습니다. :)
Alex Lop.

4

컴파일러에는 항상 일부 컨텍스트 정보가 없습니다. 예를 들어, double 값은 2를 절대로 두지 않는다는 것을 알고있을 것입니다. 이는 수학 함수의 기능이기 때문에 라이브러리에서 사용하기 때문입니다. 컴파일러는 라이브러리에서 코드를 볼 수 없으며 모든 수학적 함수의 모든 기능을 알 수 없으며이를 구현하는 길고 복잡한 모든 방법을 감지 할 수 없습니다.


4

컴파일러가 전체 프로그램을 반드시 볼 필요는 없습니다. 공유 라이브러리를 호출하는 프로그램을 가질 수 있습니다. 공유 라이브러리는 직접 호출되지 않는 프로그램의 함수를 다시 호출합니다.

따라서 라이브러리가 런타임에 변경되면 컴파일 된 라이브러리와 관련하여 죽은 기능이 활성화 될 수 있습니다.


3

경우 컴파일러는 정확하게 모든 죽은 코드를 제거 할 수 있습니다, 그것은 호출됩니다 인터프리터 .

이 간단한 시나리오를 고려하십시오.

if (my_func()) {
  am_i_dead();
}

my_func() 는 임의의 코드를 포함 할 수 있으며 컴파일러가 true 또는 false를 리턴하는지 여부를 판별하려면 코드를 실행하거나 코드 실행과 기능적으로 동등한 것을 수행해야합니다.

컴파일러의 아이디어는 코드의 부분 분석 만 수행하여 별도의 실행 환경 작업을 단순화한다는 것입니다. 전체 분석을 수행하면 더 이상 컴파일러가 아닙니다.


당신은 함수로 컴파일러를 고려하는 경우 c(), c(source)=compiled code등과 같은 실행 환경 r(), r(compiled code)=program output다음의 값을 계산해야 할 모든 소스 코드에 대한 출력을 결정하기 위해, r(c(source code)). 산출하는 경우 c()의 가치에 대한 지식이 필요합니다 r(c())어떤 입력을 별도 대한 필요가 없습니다 r()c(): 당신이 함수를 도출 단지 수 i()에서 c()같은 것을 i(source)=program output.


2

다른 사람들은 중단 문제 등에 대해 논평했습니다. 이들은 일반적으로 기능의 일부에 적용됩니다. 그러나 전체 유형 (클래스 / 등)이 사용되는지 여부를 아는 것은 어렵거나 불가능할 수 있습니다.

.NET / Java / JavaScript 및 기타 런타임 기반 환경에서는 리플렉션을 통해로드되는 중지 유형이 없습니다. 이것은 의존성 주입 프레임 워크에 널리 사용되며 역 직렬화 또는 동적 모듈로드에 대해 추론하기가 훨씬 어렵습니다.

컴파일러는 이러한 유형의로드 여부를 알 수 없습니다. 그들의 이름 런타임에 외부 구성 파일에서 올 있습니다.

사용하지 않는 코드의 하위 그래프를 안전하게 제거하려고하는 도구의 일반적인 용어 인 트리 흔들림 을 검색 할 수 있습니다 .


Java와 Javascript에 대해서는 잘 모르지만 .NET에는 실제로 이러한 종류의 DI 감지 (에이전트 멀더라고 함)를위한 더 선명한 플러그인이 있습니다. 물론 구성 파일을 감지 할 수는 없지만 코드의 적합성을 감지 할 수 있습니다 (이는 더 널리 사용됨).
Ties

2

기능을 수행

void DoSomeAction(int actnumber) 
{
    switch(actnumber) 
    {
        case 1: Action1(); break;
        case 2: Action2(); break;
        case 3: Action3(); break;
    }
}

당신은이 증명할 수 actnumber없을 것 2있도록 Action2()호출되지 않습니다 ...?


7
함수의 호출자를 분석 할 수 있다면 가능할 것입니다.
abligh

2
@abligh 그러나 컴파일러는 일반적으로 모든 호출 코드를 분석 할 수 없습니다. 어쨌든 전체 분석에는 가능한 모든 제어 흐름의 시뮬레이션이 필요할 수 있으며, 이는 필요한 리소스와 시간으로 인해 거의 항상 불가능합니다. 따라서 이론적으로 ' 결코 불려지지 않을 것'이라는 증거 있더라도 Action2()실제로 주장을 증명하는 것은 불가능합니다 . 컴파일러가이를 완전히 해결할 수는 없습니다 . 차이점은 '숫자 X가 있습니다'와 '숫자 X를 십진수로 쓸 수 있습니다'와 같습니다. 일부 X의 경우 전자는 사실이지만 후자는 절대 발생하지 않습니다.
CiaPan

이것은 나쁜 대답입니다. 다른 답변 은 여부를 알 수 없다는 것을 증명 합니다 actnumber==2. 이 답변은 단지 복잡성을 언급하지 않고 어렵다고 주장합니다.
MSalters

1

중지 문제에 동의하지 않습니다. 실제로는 결코 도달하지 않더라도 그러한 코드를 죽은 것으로 호출하지는 않습니다.

대신 다음을 고려하십시오.

for (int N = 3;;N++)
  for (int A = 2; A < int.MaxValue; A++)
    for (int B = 2; B < int.MaxValue; B++)
    {
      int Square = Math.Pow(A, N) + Math.Pow(B, N);
      float Test = Math.Sqrt(Square);
      if (Test == Math.Trunc(Test))
        FermatWasWrong();
    }

private void FermatWasWrong()
{
  Press.Announce("Fermat was wrong!");
  Nobel.Claim();
}

(유형 및 오버플로 오류 무시) 죽은 코드?


2
Fermat의 마지막 정리는 1994 년에 입증되었습니다. 따라서 올바른 메소드 구현은 FermatWasWrong을 절대 실행하지 않습니다. 부동 소수점의 정밀도 한계에 도달 할 수 있기 때문에 구현이 FermatWasWrong을 실행할 것이라고 생각합니다.
Taemyr

@Taemyr Aha! 이 프로그램은 Fermat의 마지막 정리를 올바르게 테스트하지 않습니다. 테스트 대상에 대한 반례는 N = 3, A = 65536, B = 65536 (Test = 0)
user253751

@immibis 예, 플로트의 정밀도가 문제가되기 전에 int가 오버플로되는 것을 놓쳤습니다.
Taemyr

@immibis 내 게시물의 하단에 유의하십시오 : 유형 및 오버플로 오류를 무시하십시오. 나는 결정의 기초로 해결되지 않은 문제라고 생각한 것을 취하고 있었다. 나는 코드가 완벽하지 않다는 것을 안다. 어쨌든 무차별 강요 할 수없는 문제입니다.
Loren Pechtel

-1

이 예를보십시오 :

public boolean isEven(int i){

    if(i % 2 == 0)
        return true;
    if(i % 2 == 1)
        return false;
    return false;
}

컴파일러는 int가 짝수 또는 홀수 일 수 있음을 알 수 없습니다. 따라서 컴파일러는 코드의 의미를 이해할 수 있어야합니다. 어떻게 구현해야합니까? 컴파일러는 최저 수익이 절대로 실행되지 않도록 보장 할 수 없습니다. 따라서 컴파일러는 죽은 코드를 감지 할 수 없습니다.


1
음? C # + ReSharper로 작성하면 몇 가지 힌트가 나타납니다. 그것들을 따라 가면 마침내 코드가 나옵니다 return i%2==0;.
Thomas Weller

10
당신의 예는 너무 단순해서 설득력이 없습니다. 의 구체적인 사례 i % 2 == 0i % 2 != 0도 정수의 가치에 대해 추론을 필요로하지 않습니다가 (여전히 쉽게하는 것입니다) 상수를 모듈로, 그것은 단지 공통 부분 식 제거 및 일반 원칙 (정규화도) 필요 if (cond) foo; if (!cond) bar;로 단순화 할 수 있습니다 if (cond) foo; else bar;. 물론 "시맨틱 스를 이해하는 것"은 매우 어려운 문제이지만,이 게시물은 그것이 문제임을 보여 주지도 않으며,이 어려운 문제를 해결하는 것이 데드 코드 감지에 필요하다는 것을 보여 주지도 않습니다.

5
귀하의 예에서 최적화 컴파일러는 공통 하위 표현식을 i % 2찾아 임시 변수로 가져옵니다. 그런 다음 두 if명령문이 상호 배타적이며로 쓸 수 있음 을 인식 if(a==0)...else...한 다음 가능한 모든 실행 경로가 첫 번째 두 return명령문을 통과 하므로 세 번째 return명령문은 죽은 코드임을 발견합니다. ( 좋은 최적화 컴파일러는 더욱 공격적입니다. GCC는 테스트 코드를 한 쌍의 비트 조작 조작으로 바꿨습니다).
Mark

1
이 예는 나에게 좋습니다. 컴파일러가 실제 상황에 대해 알지 못하는 경우를 나타냅니다. 동일합니다 if (availableMemory()<0) then {dead code}.
리틀 산티

1
@LittleSanti : 실제로 GCC는 사용자가 작성한 모든 것이 죽은 코드 임을 감지합니다 ! 그것은 단지 {dead code}부분 이 아닙니다 . GCC는 피할 수없는 부호있는 정수 오버플로가 있음을 증명함으로써이를 발견합니다. 따라서 실행 그래프에서 해당 호의 모든 코드는 데드 코드입니다. GCC는 해당 아크로 이어지는 조건 분기를 제거 할 수도 있습니다.
MSalters
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.