En programación informática , un bucle infinito (o bucle sin fin ) [1] [2] es una secuencia de instrucciones que, tal como está escrita, continuará sin fin, a menos que se produzca una intervención externa, como apagar la energía mediante un interruptor o desenchufar un enchufe. Puede ser intencional.
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 se diferencia 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 :
cuantos = 0 mientras hay_mas_datos () hacer cuantos = cuantos + 1 fin mostrar "la cantidad de elementos contados = " cuantos
Las mismas instrucciones se ejecutaron continuamente hasta que fueron detenidas o interrumpidas ... por el FALSO devuelto en algún momento por la función is_there_more_data .
Por el contrario, el siguiente bucle no finalizará por sí solo:
pájaros = 1 pez = 2 mientras pájaros + pez > 1 hacer pájaros = 3 - pájaros pez = 3 - pez fin
Los pájaros alternarán entre ser 1 y 2, mientras que los peces alternarán entre ser 2 y 1. El bucle no se detendrá a menos que se produzca una intervención externa ("desenchufar").
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 una que hace que el bucle comience de nuevo. En sistemas operativos más antiguos con multitarea cooperativa , [5] los bucles infinitos normalmente causaban que todo el sistema dejara de responder. Con el modelo de multitarea preventiva ahora predominante, los bucles infinitos generalmente hacen que el programa consuma todo el tiempo de procesador disponible, pero generalmente un usuario puede terminarlos. Los bucles de espera ocupada también se denominan a veces "bucles infinitos". Los bucles infinitos son una posible causa de que una computadora se cuelgue o se congele ; otras incluyen la falla de procesamiento , el bloqueo y las violaciones de acceso .
Un bucle consiste en repetir un conjunto de instrucciones hasta que se cumpla una condición específica. Un bucle infinito se produce cuando la condición nunca se cumple debido a alguna característica inherente del bucle.
Existen algunas situaciones en las que este es el comportamiento deseado. Por ejemplo, los juegos de las consolas basadas en cartuchos normalmente no tienen una condición de salida en su bucle principal, ya que no hay un sistema operativo al que el programa pueda salir; el bucle se ejecuta hasta que se apaga la consola.
Las computadoras interactivas modernas requieren que la computadora esté constantemente monitoreando la entrada 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 apague o se reinicie. En la computadora de guía Apollo , por ejemplo, este bucle externo estaba contenido en el programa Exec, [6] y si la computadora no tenía absolutamente ningún otro trabajo que hacer, ejecutaba en bucle un trabajo ficticio que simplemente apagaba la luz indicadora de "actividad de la computadora".
Las computadoras modernas tampoco suelen detener los relojes que controlan el circuito del procesador o la placa base cuando fallan, sino que vuelven a una condición de error que muestra mensajes al operador (como la pantalla azul de la muerte ) y entran en un bucle infinito a la espera de que el usuario responda a una solicitud 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 bloqueos tradicionales que ponen a un subproceso a dormir cuando no puede adquirir el bloqueo, los spinlocks "giran" repetidamente en un bucle infinito hasta que el bloqueo 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 los mecanismos de sincronización de nivel superior, como los mutex .
En programas multiproceso, algunos subprocesos pueden ejecutarse dentro de bucles infinitos sin que el programa entero quede atascado en un bucle infinito. Si el subproceso principal sale, todos los subprocesos del proceso se detienen forzosamente, por lo que toda la ejecución termina y el proceso/programa finaliza. Los subprocesos dentro de los bucles infinitos pueden realizar tareas de "mantenimiento" o pueden estar en un estado bloqueado esperando una entrada (del socket/cola) y reanudar la ejecución cada vez que se recibe una entrada.
La mayoría de las veces, el término se utiliza para aquellas situaciones en las que este no es el resultado esperado; es decir, cuando se trata de un error . [7] Estos errores son más comunes entre los programadores novatos, pero también pueden ser cometidos por 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 enlaces formados incorrectamente pueden crear un bucle de referencia en la estructura de datos, donde un nodo se vincula a otro que aparece antes en la secuencia. Esto convierte parte de la estructura de datos en un anillo , lo que hace que el código ingenuo se repita eternamente.
Si bien la mayoría de los bucles infinitos se pueden detectar 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; esta es la indecidibilidad del problema de la detención . [8]
Mientras el sistema responda, los bucles infinitos a menudo se pueden interrumpir enviando una señal al proceso (como SIGINT en Unix), o una interrupción al procesador, lo que hace que se cancele 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 el proceso puede no responder a las señales o el procesador puede estar en un estado ininterrumpible, como en el error de coma de Cyrix (causado por la superposición de instrucciones ininterrumpibles en una tubería 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 sin apagar el sistema.
Los bucles infinitos se pueden implementar utilizando varias construcciones de flujo de control . Lo más común, en la programación no estructurada, es volver a saltar hacia arriba ( goto ), mientras que en la programación estructurada es un bucle indefinido (bucle while) configurado para que nunca finalice, ya sea omitiendo la condición o estableciéndola explícitamente como verdadera, como while (true) ...
.
Algunos lenguajes tienen construcciones especiales para bucles infinitos, generalmente omitiendo la condición de un bucle indefinido. Algunos ejemplos son Ada ( loop ... end loop
), [10] Fortran ( DO ... END DO
), Go ( for { ... }
), Ruby ( loop do ... end
) y Rust ( loop { ... }
).
Un ejemplo sencillo (en C ):
#incluir <stdio.h> int main () { for (;;) // o equivalentemente, while (1) printf ( "Bucle infinito \n " ); return 0 ; }
La forma for (;;)
de un bucle infinito es tradicional, aparece en la referencia estándar The C Programming Language y a menudo se pronuncia con un juego de palabras "por siempre". [11]
Este es un bucle que imprimirá "Bucle infinito" 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 envía incondicionalmente la ejecución a la primera.
Un ejemplo en Java :
mientras ( verdadero ) { Sistema . out . println ( "Bucle infinito" ); }
El bucle while nunca termina porque su condición siempre es verdadera.
Un ejemplo en Bourne Again Shell :
para (( ;; )) ; hacer eco "Bucle infinito" hecho
Un ejemplo en Rust :
loop { println! ( "Bucle infinito" ); }
A continuación se muestra un ejemplo de un bucle infinito en Visual Basic :
dim x como entero hacer mientras 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 comienzo del código del bucle, x
se le asigna el valor de 1 (sin importar cualquier valor anterior) antes de que se cambie a x
+ 1. Por lo tanto, el bucle siempre dará como resultado x
= 2 y nunca se interrumpirá. Esto se puede solucionar 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 de los programadores sobre los símbolos matemáticos puede provocar un bucle infinito no intencionado. Por ejemplo, aquí se muestra un fragmento en C :
#incluir <stdio.h> int main ( void ) { int a = 0 ; while ( a < 10 ) { printf ( "%d \n " , a ); if ( a = 5 ) printf ( "a es igual a 5! \n " ); a ++ ; } return 0 ; }
La salida esperada son los números del 0 al 9, con un "a es igual a 5" intercalado 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
en este punto del programa. Por lo tanto, a
nunca podrá avanzar hasta 10 y este bucle no puede terminar.
Este problema también puede deberse a un comportamiento inesperado al evaluar la condición de terminación. A continuación, se muestra un ejemplo en C :
flotante 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, lo que introduce errores de redondeo en cada incremento (véase el recuadro).
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 desigualdad fallen inesperadamente, es más seguro usar pruebas de mayor que o menor que cuando se trabaja con valores de punto flotante. Por ejemplo, en lugar de probar si es x
igual a 1,1, se podría probar si (x <= 1.0)
, o (x < 1.1)
, cualquiera de los cuales sería seguro que salga después de un número finito de iteraciones. Otra forma de solucionar este ejemplo en particular sería usar un entero como índice de bucle , contando el número de iteraciones que se han realizado.
Un problema similar ocurre con frecuencia en el análisis numérico : para calcular un determinado resultado, se intenta realizar una iteración hasta que el error sea menor que una tolerancia elegida. Sin embargo, debido a los errores de redondeo durante la iteración, la tolerancia especificada nunca se puede alcanzar, lo que da como resultado un bucle infinito.
Un bucle infinito puede ser causado por la interacción de varias entidades. Consideremos un servidor que siempre responde con un mensaje de error si no entiende la solicitud. Incluso si no existe la posibilidad de un bucle infinito dentro del propio servidor, un sistema que comprenda dos de ellos ( A y B ) puede repetirse 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 entiende el mensaje de error, responde a A con su propio mensaje de error; si A no entiende el mensaje de error de B , envía otro mensaje de error, y así sucesivamente.
Un ejemplo común de este tipo de situación es un bucle de correo electrónico. Un ejemplo de un bucle de correo electrónico es cuando alguien recibe un correo de una bandeja de entrada sin respuesta, pero su respuesta automática está activada. Responderá a la bandeja de entrada sin respuesta, lo que activará la respuesta "esta es una bandeja de entrada sin respuesta". Esta respuesta se enviará al usuario, quien luego enviará una respuesta automática a la bandeja de entrada sin respuesta, y así sucesivamente.
Un bucle pseudoinfinito es un bucle que parece infinito pero en realidad es sólo un bucle muy largo.
Un ejemplo en bash :
para x en $( seq 1000000000 ) ; hacer código de bucle terminado
Un ejemplo de bucle en C :
unsigned int i ; 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 un unsigned int
y agregar 1 a ese número hará que el bucle vuelva a 0, rompiendo el bucle. El límite real de i
depende de los detalles del sistema y del compilador utilizados. Con aritmética de precisión arbitraria , este bucle continuaría hasta que la memoria de la computadora ya no pudiera contener i
. Si i
fuera un entero con signo, en lugar de un entero sin signo, el desbordamiento sería indefinido. En este caso, el compilador podría optimizar el código en un bucle infinito.
La recursión infinita es un caso especial de un bucle infinito que es causado por la recursión .
El siguiente ejemplo en Visual Basic para Aplicaciones (VBA) devuelve un error de desbordamiento de pila :
Sub Test1 () Llamar a Test1 Fin Sub
A primera vista, un while (true)
bucle " " parece infinito, pero puede haber una forma de escapar del bucle mediante una sentencia break o return . Ejemplo en PHP :
mientras ( verdadero ) { si ( $foo -> bar ()) { devolver ; } }
El bucle de Alderson es un término poco común en la jerga o el argot que se utiliza para referirse a un bucle infinito en el que hay una condición de salida disponible, pero inaccesible en una implementación del código, generalmente debido a un error del programador. Son los más comunes y visibles durante la depuración del código de la interfaz de usuario .
Un ejemplo de pseudocódigo similar a C de un bucle Alderson, donde se supone que el programa suma los números dados por el usuario hasta que se da cero, pero donde se utiliza el operador incorrecto:
int suma = 0 ; int i ; while ( true ) { printf ( "Ingrese un número para agregar a la suma o 0 para salir" ); i = getUserInput (); if ( i * 0 ) { // si i por 0 es verdadero, agregue i a la suma. Nota: CERO significa FALSO, distinto de cero significa VERDADERO. "i * 0" es CERO (FALSO)! suma += i ; // 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 ( suma > 100 ) { break ; // terminar el bucle; la condición de salida existe pero nunca se alcanza porque la suma nunca se agrega a } }
El término supuestamente recibió su nombre de un programador (de 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.
computación .. un defecto .. que .. para hacer un bucle
Tan pronto como se cierra el shell de comandos con una combinación de control-c...