java - descargar - mientras(verdadero); loop lanza código inalcanzable cuando no está en un vacío
java c++ descargar (4)
En general, no es posible determinar con absoluta certeza si algo es alcanzable o no.
¿Por qué? Es el equivalente al problema de detención .
El problema de detención pregunta:
Dada la descripción de un programa informático arbitrario, decida si el programa finaliza la ejecución o continúa ejecutándose para siempre.
Este problema ha demostrado ser insoluble.
Si el fragmento de código X es alcanzable o no es lo mismo que decir si el código anterior se detendrá.
Debido a que es un problema sin solución, el compilador (en Java o en cualquier otro idioma) no intenta resolverlo. Si sucede que determina que realmente es inalcanzable, entonces obtienes la advertencia. Si no, puede o no ser alcanzable.
En Java, el código inalcanzable es un error del compilador. Por lo tanto, para mantener la compatibilidad, la especificación del idioma define exactamente "qué tan difícil" debería intentar el compilador. (Que según las otras respuestas, es "no entre dentro de otra función").
En otros lenguajes (como C ++), el compilador puede ir más allá sujeto a optimizaciones. (Se puede detectar el código inalcanzable después de subrayar la función y descubrir que nunca vuelve).
Estaba haciendo algunos pequeños programas en Java. Sé que si escribo while(true);
el programa se congelará en este ciclo . Si el código es así:
Prueba 1:
public class While {
public static void main(String[] args) {
System.out.println("start");
while (true);
System.out.println("end");
}
}
El compilador me arroja el error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unreachable code
at While.main(While.java:6)
No sabía que este error existe. Pero entiendo por qué es arrojado. Por supuesto, la línea 6 era inalcanzable , causando un problema de compilación. Luego probé esto:
Prueba 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");
}
}
Por alguna razón, el programa se ejecutó normalmente (la consola imprimió "inicio" y luego se congeló). El compilador no pudo verificar dentro de void a()
y ver que no es alcanzable. Para asegurarse de que intenté:
Prueba 3:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
System.out.println("end");
}
static void a() {
while(true);
}
}
Mismo resultado que la Prueba 2.
Después de algunas investigaciones encontré esta question . Entonces, si el código dentro de los paréntesis es una variable, el compilador no lanzaría la excepción . Eso tiene sentido, pero no creo que lo mismo se aplique a los voids
.
P: Entonces, ¿por qué el compilador simplemente me arroja el error en la Prueba 1, si es void b()
(Prueba 2) y System.out.println("end");
(Prueba 3) no es alcanzable?
EDITAR: Probé la Prueba 1 en C ++:
#include <iostream>
using namespace std;
int main()
{
cout << "start" << endl;
while(true);
cout << "end" << endl;
return 0;
}
El compilador no arrojó ningún error , entonces obtuve el mismo resultado que el Test 2 y el Test 3. Entonces, ¿supongo que es algo de Java?
La especificación de idioma tiene una definición exacta de lo que el compilador debería tratar como código inalcanzable, consulte también https://.com/a/20922409/14955 .
En particular, no le importa si un método se completa y no mira dentro de otros métodos.
No hará más que eso.
Sin embargo, puede obtener herramientas de análisis de código estático como FindBugs para obtener un análisis "más profundo" (aunque no está seguro si detectan el patrón que describió y, como lo han señalado otros, el problema de detención en generalidad no puede ser resuelto algorítmicamente de todos modos, por lo que uno tiene que trazar la línea en alguna definición razonable de "mejor esfuerzo").
La respuesta se encuentra en las reglas establecidas para la accesibilidad según la Especificación del lenguaje Java . Primero dice
Es un error en tiempo de compilación si una instrucción no se puede ejecutar porque es inalcanzable.
Y entonces
Una instrucción
while
puede completar normalmente si al menos uno de los siguientes es verdadero:
- La instrucción
while
es alcanzable y la expresión de condición no es una expresión constante (§15.28) con valortrue
.- Hay una declaración de
break
alcanzable que sale de la instrucción while.
y
Una declaración de expresión puede completarse normalmente si es alcanzable.
En su primer ejemplo, tiene un ciclo while que no puede completarse normalmente porque tiene una condición que es una expresión constante con valor true
y no hay break
alcanzable dentro de ella.
En su segundo y tercer ejemplo, la declaración de expresión (invocación de método) es alcanzable y, por lo tanto, puede completarse normalmente.
Entonces, supongo que esto es algo de java.
Las reglas anteriores son las reglas de Java. C ++ probablemente tenga sus propias reglas, al igual que otros lenguajes.
El código inalcanzable es un error de tiempo de compilación que simplemente dice ''el flujo de este programa no tiene sentido; algo nunca se alcanzará '' .
Obviamente, sus pruebas funcionan como lo hacen debido a un bucle infinito, pero ¿por qué la primera falla con un error de tiempo de compilación?
Una instrucción while puede completarse normalmente si al menos uno de los siguientes es verdadero:
La instrucción while es alcanzable y la expresión de condición no es una expresión constante ( §15.28 ) con valor verdadero.
Hay una declaración de interrupción alcanzable que sale de la instrucción while.
De acuerdo, pero ¿qué pasa con las invocaciones de métodos (como a()
)? ¿Por qué las pruebas 2 y 3 se compilan con éxito?
- Una declaración de expresión puede completarse normalmente si es alcanzable.
Como una invocación a un método se considera una expresión, siempre será alcanzable mientras no haya nada antes de que bloquee su ruta de ejecución lógica.
Para ilustrar mejor algunos razonamientos detrás de este mecanismo de compilación, tomemos una declaración if
, por ejemplo.
if(false)
System.out.println("Hello!"); // Never executes
Lo anterior será correcto en tiempo de compilación (¡aunque muchos IDE definitivamente gimotearán!).
La especificación de Java 1.7 habla de esto :
La razón de este tratamiento diferente es permitirles a los programadores definir "variables de bandera" tales como:
static final boolean DEBUG = false;
y luego escribe un código como:
if (DEBUG) { x=3; }
La idea es que sea posible cambiar el valor de DEPURAR de falso a verdadero o de verdadero a falso y luego compilar el código correctamente sin ningún otro cambio en el texto del programa.
Además, en realidad hay una razón de compatibilidad con versiones anteriores:
Esta capacidad de "compilar condicionalmente" tiene un impacto significativo y una relación con la compatibilidad binaria ( §13 ). Si se compila un conjunto de clases que usan dicha variable "flag" y se omite el código condicional, no bastará con distribuir una nueva versión de la clase o interfaz que contenga la definición de la bandera. Por lo tanto, un cambio en el valor de una bandera no es compatible con binarios binarios preexistentes ( §13.4.9 ). (También hay otras razones para dicha incompatibilidad, como el uso de constantes en las etiquetas de los casos en las sentencias switch, ver §13.4.9 ).
La mayoría (por la especificación), si no todas, las implementaciones del compilador de Java no atraviesan los métodos. Al analizar su código Java, ve a()
como simplemente un MethodInvocationElement
, que significa ''Este código llama a otro código. Realmente no me importa, solo estoy mirando la sintaxis ". . Sintácticamente , tiene sentido que el código subsiguiente pertenezca después de una llamada a a()
.
Tenga en cuenta los costos de rendimiento. La compilación ya lleva una cantidad considerable de tiempo. Para mantener las cosas rápidas, el compilador de Java no recurre en los métodos; eso tomaría años (el compilador tendría que evaluar muchas, muchas rutas de código, en teoría).
Reiterar aún más que es sintácticamente impulsado es agregar una return;
declaración directamente después de su ciclo en a()
. No compila, ¿verdad? Sintacticamente , sin embargo, tiene sentido sin eso.