awesome-everything EN
↑ Обратно к восхождению

Базовый CS с нуля

Циклы как повторные переходы

Суть Цикл — это обратный переход: выполнить тело, проверить условие, и если оно ещё истинно — прыгнуть назад на начало. while и for компилируются в один и тот же паттерн обратного перехода. Цикл бесконечен, когда переход назад безусловен или условие никогда не становится ложным.
◷ 20 min

Ты знаешь, что инструкция перехода может установить счётчик команд на любой адрес — включая адрес раньше в памяти, чем текущая инструкция. Что произойдёт, если CPU прыгнет назад к инструкции, которую он уже выполнял?

Он выполнит её снова. А если следующая инструкция снова является обратным переходом — он выполнит её в третий раз. Четвёртый. И продолжает так, пока что-то не изменится.

Это повторение и есть цикл. Не специальная аппаратная функция — у CPU нет понятия «цикл». Это просто тот же цикл выборка–декодирование–исполнение, снова и снова выполняющий те же инструкции под управлением условного перехода, который продолжает указывать счётчик команд назад, пока условие не станет ложным.

Каждый цикл while, каждый цикл for, каждое повторение в каждой программе сводится к этому одному механизму. Как только ты поймёшь обратный переход — ты поймёшь циклы на уровне, каким их видит машина.

Цель

После этого урока ты сможешь объяснить цикл как обратный переход под управлением условия, трассировать машинные шаги цикла while, описать, почему while и for имеют одинаковую базовую структуру, и объяснить точно, почему возникает бесконечный цикл.

1

Обратный переход: возврат к более ранней инструкции. Инструкция перехода может адресоваться на любой адрес — вперёд или назад в памяти. Обратный переход устанавливает счётчик команд на адрес, который меньше, чем адрес самой инструкции перехода. Следующая выборка приходит из этого более раннего адреса, и CPU заново выполняет всё между ним и переходом.

Нет ничего особенного в обратном переходе для аппаратуры CPU. Это тот же опкод JUMP, что и прямой переход. Единственное отличие — величина и направление смещения цели: отрицательное смещение = назад, положительное = вперёд. CPU просто устанавливает СК в адрес цели и возобновляет свой цикл.

Обратный переход полезен в паре с условным тестом. Если прыгать назад безусловно — сразу получается бесконечный цикл. Если прыгать назад только когда условие выполнено — получается управляемое повторение: тело выполняется снова только пока условие остаётся истинным.

2

Структура цикла while в машинном коде. while (условие) { тело } компилируется в следующую машинную структуру:

[LOOP_TOP: CMP ...]              ; проверить условие
[условный переход на LOOP_END]   ; выйти, если условие ЛОЖНО
[инструкции тела]                ; тело цикла
[безусловный JUMP на LOOP_TOP]   ; обратный переход к тесту
[LOOP_END: ...]                  ; продолжение после цикла

Ключевое наблюдение: тест стоит первым, перед телом. На каждой итерации CPU:

  1. Оценивает условие с помощью инструкции сравнения, обновляя флаги.
  2. Проверяет флаг условным переходом — если условие ложно, переход срабатывает и CPU выходит из цикла (прямой переход на LOOP_END). Если условие истинно — fall-through.
  3. Выполняет тело цикла.
  4. Доходит до безусловного обратного перехода и устанавливает СК обратно на LOOP_TOP.
  5. Возвращается к шагу 1.

Обратный переход в конце цикла всегда безусловный. Вся логика решения находится в условном переходе в начале.

Почему это работает

Почему условие проверяется в начале? Потому что while (условие) не должен выполнять тело вообще, если условие уже ложно при первом достижении цикла. Проверка в начале гарантирует, что тело выполнится ноль раз, когда условие начинается ложным. Это отличается от цикла do { тело } while (условие), где тело выполняется хотя бы один раз, потому что тест стоит в конце. На машинном уровне единственное отличие — стоит ли пара CMP + условный переход до или после инструкций тела.

3

Структура цикла for: тот же машинный код, другой синтаксис. for (init; условие; обновление) { тело } — это синтаксический сахар, удобство на уровне источника. После компиляции это та же структура с обратным переходом, что и цикл while:

[инструкции инициализации]       ; выполняются один раз до начала цикла
[LOOP_TOP: CMP ... условие ...]
[условный переход на LOOP_END]
[инструкции тела]
[инструкции обновления]          ; инкремент/декремент внизу
[безусловный JUMP на LOOP_TOP]
[LOOP_END: ...]

Код инициализации выполняется один раз перед первой итерацией. Код обновления выполняется в конце каждой итерации, непосредственно перед обратным переходом. Тест и обратный переход стоят ровно в тех же позициях, что и в цикле while. На машинном уровне CPU не может отличить скомпилированный while от скомпилированного for — они дают одинаковый паттерн инструкций.

4

Бесконечные циклы: когда обратный переход не останавливается. Цикл работает вечно, когда условие, проверяемое перед каждым обратным переходом, никогда не становится ложным. Это может произойти по двум причинам:

  1. Безусловный обратный переход: у «цикла» вообще нет условия — переход в конце всегда срабатывает, СК всегда сбрасывается на начало, и CPU никогда не выходит. Это while (true), скомпилированный в простой JUMP (без CMP, без условия), — намеренно используется в серверных и событийных циклах.

  2. Условие, которое никогда не становится ложным: тест существует, но тело цикла никогда не изменяет сравниваемые значения, поэтому условие остаётся истинным всегда. Например, если цикл отсчитывает от 10, но ошибка в теле мешает счётчику убывать, CMP всегда видит то же значение, флаг не меняется, и обратный переход всегда срабатывает.

Бесконечный цикл — не аппаратная ошибка. CPU делает ровно то, что ему говорит программа: выполняет обратный переход, сбрасывает СК, выполняет тело и снова прыгает назад — бесконечно. CPU продолжит, пока его не остановит ОС или прерывание.

CMP…
100
JGE 124
104
ADD…
108
SUB R2…
112
STORE…
116
JUMP 100
120
NEXT…
124
Цикл while в памяти. CMP по адресу 100 проверяет условие. JGE по адресу 104 прыгает на 124 (выход), когда условие ложно. Тело выполняется на 108–116. JUMP по адресу 120 — обратный переход: он сбрасывает СК на 100, повторяя тест. Стрелка от 120 назад к 100 — это цикл.
Разбор примера

Трассировка простого цикла обратного отсчёта.

Программа: отсчёт от 3 до 0, остановка когда счётчик достигает 0.

Адрес  Инструкция         Эффект
100    LOAD R0, 200       R0 ← значение счётчика (начинается с 3)
104    CMP R0, 0          сравнить R0 с 0, обновить флаги Z и N
108    JE 120             прыжок на 120 (выход), если Z=1 (R0 == 0)
112    SUB R0, 1          R0 ← R0 − 1
116    JUMP 104           обратный переход к тесту по адресу 104
120    ...                цикл завершён; продолжение здесь

Итерация 1 (R0 = 3):

  • CMP по адресу 104: 3 − 0 = 3. Z = 0, N = 0. СК → 108.
  • JE по адресу 108: Z = 0, условие «равно» ложно. Fall-through. СК → 112.
  • SUB по адресу 112: R0 = 3 − 1 = 2. СК → 116.
  • JUMP по адресу 116: обратный переход. СК ← 104.

Итерация 2 (R0 = 2):

  • CMP по адресу 104: 2 − 0 = 2. Z = 0. СК → 108.
  • JE по адресу 108: Z = 0, fall-through. СК → 112.
  • SUB по адресу 112: R0 = 2 − 1 = 1. СК → 116.
  • JUMP по адресу 116: СК ← 104.

Итерация 3 (R0 = 1):

  • CMP по адресу 104: 1 − 0 = 1. Z = 0. СК → 108.
  • JE по адресу 108: Z = 0, fall-through. СК → 112.
  • SUB по адресу 112: R0 = 1 − 1 = 0. СК → 116.
  • JUMP по адресу 116: СК ← 104.

Итерация 4 (R0 = 0 — условие выхода):

  • CMP по адресу 104: 0 − 0 = 0. Z = 1. СК → 108.
  • JE по адресу 108: Z = 1, условие «равно» истинно. Переход. СК ← 120.
  • Цикл завершён. R0 = 0.

Тело выполнилось ровно 3 раза. Обратный переход по адресу 116 сработал 3 раза. На 4-м тесте флаг Z наконец стал 1, и условный переход вышел из цикла.

Частая ошибка

Распространённое заблуждение — считать, что цикл «знает, сколько раз он выполнится». CPU этого не знает. На каждой итерации CPU проверяет условие, видит значение флага и принимает одно бинарное решение (переход или fall-through). У него нет памяти о том, сколько раз он уже прыгал назад. Количество итераций возникает из того, сколько раз условие было проверено и оказалось истинным — CPU ничего не считает; только изменение условия завершает цикл.

Практика 0 / 5

Обратный переход устанавливает СК на адрес, меньший чем адрес инструкции перехода. После обратного перехода с адреса 120 на адрес 100 чему равен СК?

В цикле while, скомпилированном в машинный код, где стоит инструкция CMP относительно тела цикла?

Цикл while со счётчиком начинается с 5, уменьшается на 1 при каждой итерации, останавливается, когда счётчик достигает 0. Сколько раз срабатывает обратный переход?

В машинном коде есть ли аппаратное отличие между циклом while и циклом for?

У цикла нет инструкции сравнения — только безусловный JUMP назад на начало. Сколько итераций он выполняет?

Проверь себя
Викторина

Каков машинный механизм, заставляющий цикл while повторять своё тело?

Итог

Цикл — это обратный переход — инструкция перехода, адрес цели которой находится раньше в памяти, чем она сама. В сочетании с условным тестом обратный переход создаёт управляемое повторение: тело цикла выполняется, условие переоценивается, и если условие всё ещё истинно — СК сбрасывается на начало цикла; если ложно — условный переход выходит из цикла, прыгая вперёд за тело. Цикл while ставит тест перед телом; цикл for добавляет блок инициализации перед циклом и блок обновления перед обратным переходом, но базовый машинный паттерн идентичен. Бесконечный цикл возникает, когда обратный переход безусловен или когда проверяемое перед каждой итерацией условие никогда не становится ложным — CPU не может обнаружить или предотвратить это; он продолжает цикл обратных переходов бесконечно, пока ОС или прерывание его не остановит.

Продолжить восхождение ↑Трассировка программы
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.