Подтвердить что ты не робот

В то время как (истинно); loop throws Недоступный код, когда он не находится в пустоте

Я делал небольшие программы в java. Я знаю, что если я напишу while(true);, программа замерзнет в этом цикле. Если код такой:

Тест 1:

public class While {
    public static void main(String[] args) {
        System.out.println("start");
        while (true);
        System.out.println("end");
    }
}

Компилятор выдает мне ошибку:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Unreachable code
    at While.main(While.java:6)

Я не знал, что эта ошибка существует. Но я понял, почему он брошен. Конечно, строка 6 была недоступна, что вызвало проблему компиляции. Затем я протестировал это:

Тест 2:

public class While {
    public static void main(String[] args) {
        System.out.println("start");
        a();
        b();
    }
    static void a() {
        while(true);
    }
    static void b() {
        System.out.println("end");
    }
}

По какой-то причине программа работает нормально (консоль напечатала "start", а затем заморозилась). Компилятор не смог проверить внутри void a() и убедиться, что он недоступен. Конечно, я пробовал:

Тест 3:

public class While {
    public static void main(String[] args) {
        System.out.println("start");
        a();
        System.out.println("end");
    }
    static void a() {
        while(true);
    }
}

Тот же результат, что и Тест 2.

После некоторого исследования я нашел этот question. Итак, если код внутри скобок является переменной, компилятор не будет генерировать исключение. Это имеет смысл, но я не думаю, что то же самое относится к voids.

Q: Итак, почему компилятор просто выбросил мне ошибку в Test 1, если void b() (тест 2) и System.out.println("end"); (Тест 3) недоступен?

EDIT: Я пробовал тест 1 в С++:

#include <iostream>

using namespace std;

int main()
{
    cout << "start" << endl;
    while(true);
    cout << "end" << endl;
    return 0;
}

Компилятор не выдавал никаких ошибок, тогда я получил тот же результат, что и Test 2, и как тест 3. Итак, я полагаю, что это Java-версия?

4b9b3361

Ответ 1

спецификация языка имеет точное определение, что компилятор должен рассматривать как недостижимый код, см. также fooobar.com/questions/144317/....

В частности, неважно, завершен ли метод, и он не просматривает другие методы.

Это не будет делать больше.

Однако вы можете получить инструменты анализа статического кода, такие как FindBugs, для получения "более глубокого" анализа (не уверены, обнаруживают ли они шаблон, который вы описали, и, как было указано другими, проблему остановки в всякая общность не может быть алгоритмически решена в любом случае, поэтому нужно провести линию при некотором разумном определении "наилучшего усилия" ).

Ответ 2

В общем, невозможно с абсолютной уверенностью определить, доступно ли что-то или нет.

Почему? Это эквивалентно проблеме остановки.

Проблема с остановкой:

Учитывая описание произвольной компьютерной программы, определите, заканчивается ли выполнение программы или продолжается ли она навсегда.

Эта проблема оказалась неразрешимой.


Независимо от того, достигнут ли X-код кода, то же самое, что сказать, будет ли код до его остановки.

Поскольку это неразрешимая проблема, компилятор (на Java или любой другой язык) не очень-то сильно его решает. Если это произойдет, чтобы определить, что это действительно недостижимо, вы получите предупреждение. Если нет, это может быть или не быть доступным.

В Java недостижимый код является ошибкой компилятора. Поэтому, чтобы поддерживать совместимость, спецификация языка точно определяет "насколько сложно" компилятор должен попробовать. (Который, согласно другим ответам, "не входит в другую функцию".)

В других языках (например, С++) компилятор может перейти к оптимизации. (Недоступный код может быть обнаружен после вложения функции и обнаружения того, что он никогда не возвращается.)

Ответ 3

Недостижимый код - это ошибка времени компиляции, которая просто говорит: "поток этой программы не имеет смысла; что-то никогда не будет достигнуто".

Очевидно, что ваши тесты выполняют то, как они это делают из-за бесконечного цикла, но почему первый сбой происходит с ошибкой времени компиляции?

Оператор while может завершиться нормально, если хотя бы один из верно следующее:

  • Оператор while доступен и выражение условия не является постоянное выражение (§15.28) со значением true.

  • Существует допустимый оператор break, который завершает оператор while.

Хорошо, но как насчет методов invocations (например, a()) - почему тесты 2 и 3 успешно компилируются?

  • Оператор выражения может завершиться нормально, если он доступен.

Так как вызов метода считается выражением, он будет всегда достижим, пока ничего не блокирует свой путь логического выполнения.


Чтобы лучше проиллюстрировать некоторые соображения, лежащие в основе этого механизма компиляции, возьмем, например, оператор if.

if(false)
   System.out.println("Hello!"); // Never executes

Вышеуказанное будет правильным во время компиляции (хотя многие IDE обязательно будут скулить!).

Спецификация Java 1.7 говорит об этом:

Обоснование этого различного обращения заключается в том, чтобы позволить программистам определить "переменные флага", такие как:

static final boolean DEBUG = false;

а затем напишите код, например:

if (DEBUG) { x=3; }

Идея состоит в том, что следует изменить значение DEBUG от false до true или от true до false, а затем скомпилируйте код правильно, без каких-либо изменений в тексте программы.

Кроме того, на самом деле существует и обратная совместимость:

Эта способность "условно компилировать" оказывает значительное влияние, и отношение к бинарной совместимости (§13). Если набор классов которые используют такую ​​переменную "флаг", компилируются, а условный код пропущено, позже не будет достаточно распространять только новую версию класс или интерфейс, который содержит определение флага. изменение значения флага, следовательно, не совместимо с бинарными с ранее существовавшими двоичными файлами (§13.4.9). (Есть и другие причины для такая несовместимость, как, например, использование констант в случае метки в операторах switch; см. §13.4.9.)


Большинство (за спецификацию), если не все, реализации компилятора Java не переходят в методы. При анализе вашего Java-кода он видит a() как только a MethodInvocationElement, что означает, что этот код вызывает другой код. Мне действительно все равно, я просто смотрю на синтаксис ". Синтаксически, имеет смысл использовать следующий код после вызова a().

Учитывайте стоимость работы. Компиляция уже занимает много времени. Чтобы ускорить работу, компилятор Java фактически не переписывает методы; что потребует возраста (компилятор должен был бы оценивать многие, многие пути кода - теоретически).


Чтобы еще раз подтвердить, что синтаксически управляется, нужно добавить оператор return; непосредственно после вашего цикла в a(). Не компилируется, не так ли? Синтаксически, однако, это имеет смысл без него.

Ответ 4

Ответ заключается в правилах, установленных для достижимости с помощью Java Language Specification. В нем сначала говорится

Это ошибка времени компиляции, если оператор не может быть выполнен, потому что он недоступен.

И затем

Оператор

A while может завершиться нормально, если хотя бы один из верно следующее:

  • Оператор while доступен и выражение условия не является константным выражением (§15.28) со значением true.
  • Существует допустимый оператор break, который завершает оператор while.

и

Оператор выражения может нормально завершаться, если он доступен.

В первом примере у вас есть цикл while, который не может завершиться нормально, поскольку он имеет условие, которое является константным выражением со значением true, и в нем нет достижимого break.

В вашем втором и третьем примерах выражение выражение (вызов метода) доступно и, следовательно, может завершиться нормально.


Итак, я полагаю, что это Java-вещь?

Правила выше - это правила Java. С++, вероятно, имеет свои собственные правила, как и другие языки.