В компьютерном программировании бесконечный цикл ( или бесконечный цикл ) [1] [2] — это последовательность инструкций, которая, как написано, будет продолжаться бесконечно, если только не произойдет внешнего вмешательства, например, выключения питания с помощью выключателя или выдергивания вилки из розетки. Это может быть сделано намеренно.
Не существует общего алгоритма, позволяющего определить, содержит ли компьютерная программа бесконечный цикл или нет; это проблема остановки .
Это отличается от «типа компьютерной программы, которая непрерывно выполняет одни и те же инструкции до тех пор, пока ее не остановят или не прервут». [3] Рассмотрим следующий псевдокод :
how_many = 0 while is_there_more_data () do how_many = how_many + 1 end display "количество подсчитанных элементов = " how_many
Те же инструкции выполнялись непрерывно, пока не были остановлены или прерваны ... значением FALSE , возвращаемым в какой-то момент функцией is_there_more_data .
Напротив, следующий цикл не закончится сам по себе:
птицы = 1 рыба = 2 пока птицы + рыба > 1 делают птицы = 3 - птицы рыба = 3 - рыба конец
Птицы будут попеременно становиться 1 или 2, а рыбы — 2 или 1. Цикл не остановится, если не произойдет внешнего вмешательства («выдернуть вилку»).
Бесконечный цикл — это последовательность инструкций в компьютерной программе , которая зацикливается бесконечно, либо из-за того, что цикл не имеет условия завершения, [4] имеет условие, которое никогда не может быть выполнено, либо из-за того, что цикл начинается заново. В старых операционных системах с кооперативной многозадачностью [5] бесконечные циклы обычно приводили к тому, что вся система переставала отвечать. С ныне распространенной моделью вытесняющей многозадачности бесконечные циклы обычно заставляют программу потреблять все доступное процессорное время, но обычно могут быть прекращены пользователем. Циклы ожидания занятости также иногда называют «бесконечными циклами». Бесконечные циклы являются одной из возможных причин зависания или зависания компьютера ; другие включают пробуксовку , взаимоблокировку и нарушения доступа .
Цикл — это повторение набора инструкций до тех пор, пока не будет выполнено определенное условие. Бесконечный цикл возникает, когда условие никогда не будет выполнено из-за некоторой присущей циклу характеристики.
Есть несколько ситуаций, когда это желательное поведение. Например, игры на игровых консолях на основе картриджей обычно не имеют условия выхода в своем основном цикле, поскольку нет операционной системы, в которую программа могла бы выйти; цикл выполняется до тех пор, пока консоль не будет выключена.
Современные интерактивные компьютеры требуют, чтобы компьютер постоянно следил за пользовательским вводом или активностью устройства, поэтому на каком-то фундаментальном уровне существует бесконечный цикл обработки бездействия , который должен продолжаться до тех пор, пока устройство не будет выключено или сброшено. В Apollo Guidance Computer , например, этот внешний цикл содержался в программе Exec, [6] и если компьютеру не нужно было делать абсолютно никакой другой работы, он запускал фиктивную задачу, которая просто выключала индикатор «активности компьютера».
Современные компьютеры также обычно не останавливают работу процессора или материнской платы, управляющей тактовыми генераторами, когда они выходят из строя. Вместо этого они возвращаются к состоянию ошибки, отображая сообщения оператору (например, синий экран смерти ), и входят в бесконечный цикл, ожидая, пока пользователь либо ответит на запрос о продолжении, либо сбросит устройство.
Спин-блокировки — это низкоуровневые механизмы синхронизации, используемые в параллельном программировании для защиты общих ресурсов. В отличие от традиционных блокировок, которые переводят поток в режим ожидания, когда он не может получить блокировку, спин-блокировки многократно «вращаются» в бесконечном цикле, пока блокировка не станет доступной. Это намеренное бесконечное зацикливание — преднамеренный выбор дизайна, направленный на минимизацию времени, которое поток тратит на ожидание блокировки, и на избежание накладных расходов на механизмы синхронизации более высокого уровня, такие как мьютексы .
В многопоточных программах некоторые потоки могут выполняться внутри бесконечных циклов, не заставляя всю программу застревать в бесконечном цикле. Если основной поток завершается, все потоки процесса принудительно останавливаются, таким образом, все выполнение завершается, и процесс/программа завершается. Потоки внутри бесконечных циклов могут выполнять «хозяйственные» задачи или могут находиться в заблокированном состоянии, ожидая ввода (из сокета/очереди), и возобновлять выполнение каждый раз при получении ввода.
Чаще всего этот термин используется для тех ситуаций, когда это не является предполагаемым результатом; то есть когда это ошибка . [ 7] Такие ошибки чаще всего встречаются у начинающих программистов, но их могут допускать и опытные программисты, поскольку их причины могут быть довольно неявными.
Например, одной из распространенных причин является то, что программист намеревается выполнить итерацию по последовательности узлов в структуре данных , такой как связанный список или дерево , выполняя код цикла один раз для каждого узла. Неправильно сформированные ссылки могут создать цикл ссылок в структуре данных, где один узел ссылается на другой, который встречается ранее в последовательности. Это превращает часть структуры данных в кольцо , заставляя наивный код зацикливаться навсегда.
Хотя большинство бесконечных циклов можно обнаружить путем тщательного изучения кода, не существует общего метода определения того, остановится ли данная программа когда-либо или будет работать вечно; это неразрешимость проблемы остановки . [ 8]
Пока система отзывчива, бесконечные циклы часто можно прервать, отправив сигнал процессу (например, SIGINT в Unix) или прерывание процессору, что приведет к прерыванию текущего процесса. Это можно сделать в диспетчере задач , в терминале с помощью команды Control-C [9] или с помощью команды kill или системного вызова . Однако это не всегда работает, так как процесс может не отвечать на сигналы или процессор может находиться в непрерываемом состоянии, как в случае ошибки комы Cyrix (вызванной перекрытием непрерываемых инструкций в конвейере инструкций ). В некоторых случаях могут работать другие сигналы, такие как SIGKILL , поскольку они не требуют, чтобы процесс был отзывчивым, в то время как в других случаях цикл не может быть завершен до завершения работы системы.
Бесконечные циклы могут быть реализованы с использованием различных конструкций потока управления . Чаще всего в неструктурированном программировании это прыжок назад ( goto ), тогда как в структурированном программировании это неопределенный цикл (цикл while), установленный так, чтобы никогда не заканчиваться, либо путем пропуска условия, либо явным заданием его в значение true, как while (true) ...
.
В некоторых языках есть специальные конструкции для бесконечных циклов, обычно путем исключения условия из неопределенного цикла. Примерами являются Ada ( loop ... end loop
), [10] Fortran ( DO ... END DO
), Go ( for { ... }
), Ruby ( loop do ... end
) и Rust ( loop { ... }
).
Простой пример (на языке C ):
#include <stdio.h> int main () { for (;;) // или эквивалентно, while (1) printf ( "Бесконечный цикл \n " ); return 0 ; }
Форма for (;;)
бесконечного цикла традиционна, она появляется в стандартном справочнике «Язык программирования Си » и часто каламбурно произносится как «навсегда» [11] .
Это цикл, который выведет «Infinite Loop» без остановки.
Похожий пример в BASIC 1980-х годов :
10 ПЕЧАТЬ "БЕСКОНЕЧНЫЙ ЦИКЛ" 20 ПЕРЕЙТИ К 10
Похожий пример в пакетных файлах DOS :
: A echo Бесконечный цикл goto : A
Здесь цикл совершенно очевиден, так как последняя строка безоговорочно возвращает выполнение первой.
Пример на Java :
while ( true ) { System.out.println ( " Бесконечный цикл " ) ; }
Цикл while никогда не завершается, поскольку его условие всегда истинно.
Пример в Bourne Again Shell :
for (( ;; )) ; do echo "Бесконечный цикл" done
Пример на Rust :
loop { println! ( "Бесконечный цикл" ); }
Вот один из примеров бесконечного цикла в Visual Basic :
dim x как целое число do while x < 5 x = 1 x = x + 1 цикл
Это создает ситуацию, когда x
никогда не будет больше 5, так как в начале кода цикла x
присваивается значение 1 (независимо от любого предыдущего значения) до того, как оно будет изменено на x
+ 1. Таким образом, цикл всегда будет приводить к x
= 2 и никогда не прервется. Это можно исправить, переместив x = 1
инструкцию за пределы цикла, чтобы ее начальное значение устанавливалось только один раз.
В некоторых языках путаница программиста с математическими символами может привести к непреднамеренному бесконечному циклу. Например, вот фрагмент на языке C :
#include <stdio.h> int main ( void ) { int a = 0 ; while ( a < 10 ) { printf ( "%d \n " , a ); if ( a = 5 ) printf ( "a равно 5! \n " ); a ++ ; } return 0 ; }
Ожидаемый вывод — числа от 0 до 9, с вставленным "a равно 5!" между 5 и 6. Однако в строке " if (a = 5)
" выше оператор = (присваивание) был перепутан с оператором == (проверка равенства). Вместо этого это присвоит значение 5 в a
этой точке программы. Таким образом, a
никогда не сможет перейти к 10, и этот цикл не может быть завершен.
Неожиданное поведение при оценке условия завершения также может вызвать эту проблему. Вот пример на языке C :
float x = 0.1 ; while ( x != 1.1 ) { printf ( "x = %22.20f \n " , x ); x += 0.1 ; }
В некоторых системах этот цикл выполнится десять раз, как и ожидалось, но в других системах он никогда не завершится. Проблема в том, что условие завершения цикла (x != 1.1)
проверяет точное равенство двух значений с плавающей точкой , а способ представления значений с плавающей точкой во многих компьютерах приведет к тому, что этот тест не будет пройден, поскольку они не могут точно представить значение 0,1, тем самым внося ошибки округления на каждом приращении (см. вставку).
То же самое может произойти и в Python :
x = 0,1 пока x != 1 : распечатать ( x ) x += 0,1
Из-за вероятности неожиданного сбоя тестов на равенство или неравенство безопаснее использовать тесты «больше или меньше» при работе со значениями с плавающей точкой. Например, вместо проверки того, x
равно ли 1,1, можно проверить, равно ли (x <= 1.0)
, или (x < 1.1)
, любой из которых наверняка завершится после конечного числа итераций. Другой способ исправить этот конкретный пример — использовать целое число в качестве индекса цикла , подсчитывая количество выполненных итераций.
Похожая проблема часто возникает в численном анализе : для вычисления определенного результата предполагается выполнение итерации до тех пор, пока ошибка не станет меньше выбранного допуска. Однако из-за ошибок округления во время итерации указанный допуск никогда не может быть достигнут, что приводит к бесконечному циклу.
Бесконечный цикл может быть вызван взаимодействием нескольких сущностей. Рассмотрим сервер, который всегда отвечает сообщением об ошибке, если он не понимает запрос. Даже если внутри самого сервера нет возможности для бесконечного цикла, система, состоящая из двух из них ( A и B ), может зацикливаться бесконечно: если A получает сообщение неизвестного типа от B , то A отвечает сообщением об ошибке B ; если B не понимает сообщение об ошибке, он отвечает A своим собственным сообщением об ошибке; если A не понимает сообщение об ошибке от B , он отправляет еще одно сообщение об ошибке и так далее.
Одним из распространенных примеров такой ситуации является цикл электронной почты. Примером цикла электронной почты может служить случай, когда кто-то получает почту из почтового ящика без ответа, но у него включен автоответ. Он ответит на почтовый ящик без ответа, вызвав ответ «это почтовый ящик без ответа». Это будет отправлено пользователю, который затем отправит автоответ на почтовый ящик без ответа, и так далее, и тому подобное.
Псевдобесконечный цикл — это цикл, который кажется бесконечным, но на самом деле является просто очень длинным циклом.
Пример на bash :
for x in $( seq 1000000000 ) ; do #loop code done
unsigned int i ; for ( i = 1 ; i != 0 ; i ++ ) { /* код цикла */ }
Кажется, что это будет продолжаться бесконечно, но на самом деле значение i
в конечном итоге достигнет максимального значения, которое можно сохранить в an unsigned int
, и добавление 1 к этому числу приведет к циклу 0, прерывая цикл. Фактический предел i
зависит от деталей системы и используемого компилятора . При использовании арифметики произвольной точности этот цикл будет продолжаться до тех пор, пока память компьютера не сможет больше хранить i
. Если бы i
было целым числом со знаком, а не целым числом без знака, переполнение было бы неопределенным. В этом случае компилятор мог бы оптимизировать код в бесконечный цикл.
Бесконечная рекурсия — это частный случай бесконечного цикла, вызванного рекурсией .
Следующий пример на Visual Basic для приложений (VBA) возвращает ошибку переполнения стека :
Подтест1 ( ) Вызов Тест1 Конец Подтеста
Цикл " while (true)
" на первый взгляд выглядит бесконечным, но может быть способ выйти из цикла с помощью оператора break или оператора return . Пример на PHP :
пока ( истина ) { если ( $foo -> bar ()) { возврат ; } }
Цикл Олдерсона — это редкий сленговый или жаргонный термин для бесконечного цикла, где есть условие выхода, но оно недоступно в реализации кода, как правило, из-за ошибки программиста. Они наиболее распространены и видны при отладке кода пользовательского интерфейса .
Пример псевдокода цикла Олдерсона на языке C, в котором программа должна суммировать числа, указанные пользователем, до тех пор, пока не будет получен ноль, но при этом используется неправильный оператор:
int sum = 0 ; int i ; while ( true ) { printf ( "Введите число для прибавления к сумме или 0 для выхода" ); i = getUserInput (); if ( i * 0 ) { // если i умножить на 0 — это true, добавить i к сумме. Примечание: НОЛЬ означает ЛОЖЬ, не ноль означает ИСТИНА. "i * 0" — это НОЛЬ (ЛОЖЬ)! sum += i ; // сумма никогда не меняется, потому что (i * 0) равно 0 для любого i; она изменилась бы, если бы в условии вместо * было != } if ( sum > 100 ) { break ; // завершить цикл; условие выхода существует, но никогда не достигается, потому что сумма никогда не прибавляется к } }
Термин предположительно получил свое название от программиста (фамилия Олдерсон), который в 1996 году [12] написал модальное диалоговое окно в Microsoft Access без кнопок «ОК» или «Отмена», тем самым отключая всю программу всякий раз, когда появлялось это окно. [13]
бесконечный цикл — это тот, в котором отсутствует .. условие выхода
вычисление .. дефект .. который .. зациклить
Как только командная оболочка закрывается комбинацией control-c ...