En programación de computadoras , un bucle infinito (o bucle sin fin ) [1] [2] es una secuencia de instrucciones que, tal como están escritas, continuarán sin fin, a menos que ocurra una intervención externa, como apagar la energía mediante un interruptor o desconectar un enchufe. . Puede que sea intencionado.
No existe un algoritmo general para determinar si un programa de computadora contiene un bucle infinito o no; Este es el problema de la detención .
Esto difiere de "un tipo de programa informático que ejecuta las mismas instrucciones continuamente hasta que se detiene o se interrumpe". [3] Considere el siguiente pseudocódigo :
how_many = 0 while is_there_more_data () do how_many = how_many + 1 end display "el número de elementos contados = " how_many
Las mismas instrucciones se ejecutaron continuamente hasta que se detuvieron o interrumpieron . . . por el FALSO devuelto en algún momento por la función is_there_more_data .
Por el contrario, el siguiente bucle no terminará por sí solo:
pájaros = 1 pez = 2 mientras pájaros + pez > 1 hacer pájaros = 3 - pájaros pez = 3 - pez final
las aves alternarán siendo 1 o 2, mientras que los peces alternarán siendo 2 o 1. El bucle no se detendrá a menos que ocurra una intervención externa ("desconectar").
Un bucle infinito es una secuencia de instrucciones en un programa de computadora que se repite sin fin, ya sea porque el bucle no tiene una condición de terminación, [4] tiene una que nunca se puede cumplir o porque hace que el bucle comience de nuevo. En sistemas operativos más antiguos con multitarea cooperativa , [5] los bucles infinitos normalmente provocaban que todo el sistema dejara de responder. Con el modelo multitarea preventivo que ahora prevalece, los bucles infinitos generalmente hacen que el programa consuma todo el tiempo disponible del procesador, pero generalmente un usuario puede finalizarlo. Los bucles de espera ocupados también se denominan a veces "bucles infinitos". Los bucles infinitos son una posible causa de que una computadora se cuelgue o se congele ; otros incluyen ataques , interbloqueos y violaciones de acceso .
Hacer un bucle es repetir un conjunto de instrucciones hasta que se cumpla una condición específica. Un bucle infinito ocurre cuando la condición nunca se cumplirá debido a alguna característica inherente del bucle.
Hay algunas situaciones en las que este es el comportamiento deseado. Por ejemplo, los juegos de las consolas de juegos basadas en cartuchos normalmente no tienen ninguna condición de salida en su bucle principal, ya que no hay ningún sistema operativo al que pueda salir el programa; el bucle se ejecuta hasta que se apaga la consola.
Las computadoras interactivas modernas requieren que la computadora esté constantemente monitoreando las entradas del usuario o la actividad del dispositivo, por lo que en algún nivel fundamental hay un bucle inactivo de procesamiento infinito que debe continuar hasta que el dispositivo se apaga o se reinicia. En la Computadora de Orientación Apollo , por ejemplo, este bucle externo estaba contenido en el programa Exec, [6] y si la computadora no tenía ningún otro trabajo que hacer, ejecutaba en bucle un trabajo ficticio que simplemente apagaba la "actividad de la computadora". " luz indicadora.
Las computadoras modernas tampoco suelen detener los relojes que controlan los circuitos del procesador o de la placa base cuando fallan. En lugar de eso, vuelven a caer en una condición de error que muestra mensajes al operador (como la pantalla azul de la muerte ) y entran en un bucle infinito esperando que el usuario responda a un mensaje para continuar o reinicie el dispositivo.
Los spinlocks son mecanismos de sincronización de bajo nivel que se utilizan en la programación concurrente para proteger los recursos compartidos. A diferencia de los candados tradicionales que ponen un hilo a dormir cuando no puede adquirir el candado, los spinlocks "giran" repetidamente en un bucle infinito hasta que el candado esté disponible. Este bucle infinito intencional es una elección de diseño deliberada destinada a minimizar el tiempo que un subproceso pasa esperando el bloqueo y evitar la sobrecarga de mecanismos de sincronización de nivel superior, como los mutex .
En programas de subprocesos múltiples, algunos subprocesos se pueden ejecutar dentro de bucles infinitos sin que todo el programa quede atrapado en un bucle infinito. Si el hilo principal sale, todos los hilos del proceso se detienen a la fuerza, por lo que toda ejecución finaliza y el proceso/programa finaliza. Los subprocesos dentro de los bucles infinitos pueden realizar tareas de "limpieza" o pueden estar en un estado bloqueado esperando entrada (desde socket/cola) y reanudar la ejecución cada vez que se recibe entrada.
Muy a menudo, el término se utiliza para aquellas situaciones en las que este no es el resultado deseado; es decir, cuando se trata de un error . [7] Estos errores son más comunes entre los programadores novatos, pero también los pueden cometer programadores experimentados, porque sus causas pueden ser bastante sutiles.
Una causa común, por ejemplo, es que un programador intenta iterar sobre una secuencia de nodos en una estructura de datos como una lista enlazada o un árbol , ejecutando el código de bucle una vez para cada nodo. Los vínculos mal formados pueden crear un bucle de referencia en la estructura de datos, donde un nodo se vincula con otro que ocurre anteriormente en la secuencia. Esto convierte parte de la estructura de datos en un anillo , lo que hace que el código ingenuo se repita para siempre.
Si bien la mayoría de los bucles infinitos se pueden encontrar mediante una inspección minuciosa del código, no existe un método general para determinar si un programa determinado se detendrá alguna vez o se ejecutará para siempre; ésta es la indecidibilidad del problema de la detención . [8]
Mientras el sistema responda, los bucles infinitos a menudo pueden interrumpirse enviando una señal al proceso (como SIGINT en Unix) o una interrupción al procesador, lo que provoca que se aborte el proceso actual. Esto se puede hacer en un administrador de tareas , en una terminal con el comando Control-C , [9] o usando el comando kill o la llamada al sistema . Sin embargo, esto no siempre funciona, ya que es posible que el proceso no responda a las señales o que el procesador se encuentre en un estado ininterrumpible, como en el error de coma de Cyrix (causado por la superposición de instrucciones ininterrumpibles en una canalización de instrucciones ). En algunos casos, otras señales como SIGKILL pueden funcionar, ya que no requieren que el proceso responda, mientras que en otros casos el bucle no se puede terminar antes de que se apague el sistema.
Se pueden implementar bucles infinitos utilizando varias construcciones de flujo de control . Lo más común es que en programación no estructurada se trate de un salto hacia atrás ( goto ), mientras que en programación estructurada se trata de un bucle indefinido (bucle while) configurado para no terminar nunca, ya sea omitiendo la condición o estableciéndola explícitamente en verdadero, como while (true) ...
.
Algunos lenguajes tienen construcciones especiales para bucles infinitos, normalmente omitiendo la condición de un bucle indefinido. Los ejemplos incluyen Ada ( loop ... end loop
), [10] Fortran ( DO ... END DO
), Go ( for { ... }
), Ruby ( loop do ... end
) y Rust ( loop { ... }
).
Un ejemplo simple (en C ):
#incluir <stdio.h> int main () { for (;;) // o equivalente, while (1) printf ( "Bucle infinito \n " ); devolver 0 ; }
La forma for (;;)
de un bucle infinito es tradicional, aparece en la referencia estándar El lenguaje de programación C y, a menudo, se pronuncia en forma de juego de palabras "para siempre". [11]
Este es un bucle que imprimirá "Infinite Loop" sin detenerse.
Un ejemplo similar en BASIC de la década de 1980 :
10 IMPRIMIR "BUCLE INFINITO" 20 IR A 10
Un ejemplo similar en archivos por lotes de DOS :
: Un eco Bucle Infinito ir a : A
Aquí el bucle es bastante obvio, ya que la última línea devuelve incondicionalmente la ejecución a la primera.
Un ejemplo en Java
mientras ( verdadero ) { Sistema . afuera . println ( "Bucle Infinito" ); }
Un ejemplo en Bourne Again Shell
para (( ;; )) ; hacer eco del "Bucle Infinito" hecho
Un ejemplo en Rust
bucle { imprimir! ( "Bucle infinito" ); }
Aquí hay un ejemplo de un bucle infinito en Visual Basic :
dim x como entero hacer while x < 5 x = 1 x = x + 1 bucle
Esto crea una situación en la que x
nunca será mayor que 5, ya que al inicio del código del bucle, x
se le asigna el valor de 1 (independientemente de cualquier valor anterior) antes de que se cambie a x
+ 1. Por lo tanto, el bucle siempre resultará en x
= 2 y nunca se romperá. Esto podría solucionarse moviendo la x = 1
instrucción fuera del bucle para que su valor inicial se establezca solo una vez.
En algunos lenguajes, la confusión del programador sobre los símbolos matemáticos puede provocar un bucle infinito involuntario. Por ejemplo, aquí hay un fragmento en C :
#incluir <stdio.h> int principal ( vacío ) { int a = 0 ; mientras ( a < 10 ) { printf ( "%d \n " , a ); if ( a = 5 ) printf ( "a es igual a 5! \n " ); un ++ ; } devolver 0 ; }
El resultado esperado son los números del 0 al 9, con un "¡a es igual a 5!" entre 5 y 6. Sin embargo, en la línea " if (a = 5)
" anterior, el operador = (asignación) se confundió con el operador == (prueba de igualdad). En cambio, esto asignará el valor de 5 a a
este punto del programa. Por lo tanto, a
nunca podrá avanzar a 10 y este ciclo no puede terminar.
Un comportamiento inesperado al evaluar la condición de terminación también puede causar este problema. Aquí hay un ejemplo en C :
flotador x = 0,1 ; mientras ( x != 1.1 ) { printf ( "x = %22.20f \n " , x ); x += 0,1 ; }
En algunos sistemas, este bucle se ejecutará diez veces como se esperaba, pero en otros sistemas nunca terminará. El problema es que la condición de terminación del bucle (x != 1.1)
prueba la igualdad exacta de dos valores de punto flotante , y la forma en que se representan los valores de punto flotante en muchas computadoras hará que esta prueba falle, porque no pueden representar el valor 0,1 exactamente, introduciendo así errores de redondeo en cada uno. incremento (ver cuadro).
Lo mismo puede suceder en Python :
x = 0,1 mientras x != 1 : imprimir ( x ) x += 0,1
Debido a la probabilidad de que las pruebas de igualdad o no igualdad fallen inesperadamente, es más seguro utilizar pruebas de mayor o menor que cuando se trabaja con valores de punto flotante. Por ejemplo, en lugar de probar si x
es igual a 1,1, se podría probar si (x <= 1.0)
, o (x < 1.1)
, cualquiera de los cuales seguramente saldrá después de un número finito de iteraciones. Otra forma de solucionar este ejemplo en particular sería utilizar un número entero como índice de bucle , contando el número de iteraciones que se han realizado.
Un problema similar ocurre frecuentemente en el análisis numérico : para calcular un resultado determinado, se pretende realizar una iteración hasta que el error sea menor que una tolerancia elegida. Sin embargo, debido a errores de redondeo durante la iteración, nunca se puede alcanzar la tolerancia especificada, lo que da como resultado un bucle infinito.
Un bucle infinito puede deberse a la interacción de varias entidades. Considere un servidor que siempre responde con un mensaje de error si no comprende la solicitud. Incluso si no hay posibilidad de un bucle infinito dentro del propio servidor, un sistema que comprende dos de ellos ( A y B ) puede bucle sin fin: si A recibe un mensaje de tipo desconocido de B , entonces A responde con un mensaje de error a B ; si B no comprende el mensaje de error, responde a A con su propio mensaje de error; si A no comprende el mensaje de error de B , envía otro mensaje de error más, y así sucesivamente.
Un ejemplo común de tal situación es un bucle de correo electrónico . Un ejemplo de bucle de correo electrónico es si alguien recibe un correo de una bandeja de entrada sin respuesta, pero su respuesta automática está activada. Responderán a la bandeja de entrada sin respuesta, lo que activará la respuesta "esta es una bandeja de entrada sin respuesta". Esto se enviará al usuario, quien luego enviará una respuesta automática a la bandeja de entrada de no respuesta, y así sucesivamente.
Un bucle pseudoinfinito es un bucle que parece infinito pero que en realidad es sólo un bucle muy largo.
Un ejemplo en bash :
para x en $( sec 1000000000 ) ; hacer #código de bucle hecho
Un ejemplo de bucle en C :
int sin firmar yo ; for ( i = 1 ; i ! = 0 ; i ++ ) { /* código de bucle */ }
Parece que esto continuará indefinidamente, pero de hecho el valor de i
eventualmente alcanzará el valor máximo almacenable en an unsigned int
y al agregar 1 a ese número volverá a 0, rompiendo el ciclo. El límite real i
depende de los detalles del sistema y del compilador utilizado. Con aritmética de precisión arbitraria , este bucle continuaría hasta que la memoria de la computadora ya no pudiera contenerlo i
. Si i
fuera un entero con signo, en lugar de un entero sin signo, el desbordamiento no estaría definido. En este caso, el compilador podría optimizar el código en un bucle infinito.
La recursividad infinita es un caso especial de bucle infinito causado por la recursividad .
El siguiente ejemplo en Visual Basic para Aplicaciones (VBA) devuelve un error de desbordamiento de pila :
Sub Prueba1 () Llamar a Prueba1 Fin Sub
Un while (true)
bucle " " parece infinito a primera vista, pero puede haber una forma de escapar del bucle mediante una declaración break o una declaración return . Ejemplo en PHP :
mientras ( verdadero ) { si ( $foo -> barra ()) { retorno ; } }
Bucle de Alderson es un término poco común de jerga o jerga para un bucle infinito donde hay una condición de salida disponible, pero inaccesible en una implementación del código, generalmente debido a un error del programador. Estos son los más comunes y visibles al depurar el código de la interfaz de usuario .
Un ejemplo de pseudocódigo similar a C de un bucle de Alderson, donde se supone que el programa suma números dados por el usuario hasta obtener cero, pero donde se usa el operador incorrecto:
suma entera = 0 ; int yo ; while ( true ) { printf ( "Ingrese un número para agregarlo a la suma o 0 para salir" ); i = getUserInput (); if ( i * 0 ) { // si i multiplicado por 0 es verdadero, suma i a la suma. Nota: CERO significa FALSO, distinto de cero significa VERDADERO. ¡"i * 0" es CERO (FALSO)! suma += yo ; // la suma nunca cambia porque (i * 0) es 0 para cualquier i; cambiaría si tuviéramos != en la condición en lugar de * } if ( sum > 100 ) { break ; // terminar el ciclo; La condición de salida existe pero nunca se alcanza porque la suma nunca se suma a } }
El término supuestamente recibió su nombre de un programador (apellido Alderson) que en 1996 [12] había codificado un cuadro de diálogo modal en Microsoft Access sin un botón Aceptar o Cancelar, deshabilitando así todo el programa cada vez que aparecía el cuadro. [13]
un bucle infinito es aquel que carece de... una condición de salida
informática... un defecto... que... hacer un bucle
Tan pronto como se cierra el shell de comandos con una combinación control-c...