Условные переходы и ветвления
Урок показывает, как из cmp и условного перехода собирается обычный if-else.
Условный переход прыгает только если выполнено условие на флагах; иначе исполнение идёт дальше по порядку.
Связка cmp + j-переход
Ветвление всегда состоит из двух шагов: сначала cmp выставляет флаги, потом условный переход их читает и решает, прыгать ли:
| Переход | Прыгает, если |
je / jz | равно / ноль (ZF=1) |
jne / jnz | не равно (ZF=0) |
jg / jl | больше / меньше (знаковые) |
ja / jb | больше / меньше (беззнаковые) |
Если-иначе на ассемблере
Рассмотрим C-код if (rax > 10) rbx = 1; else rbx = 0;. На ассемблере он раскладывается так:
cmp rax, 10
jg bolshe ; если rax > 10, прыгнуть в bolshe
mov rbx, 0 ; ветка else
jmp konec
bolshe:
mov rbx, 1 ; ветка if
konec:Обратите внимание: после ветки else стоит jmp konec, чтобы не «провалиться» в ветку if. Это типичная структура любого ветвления.
Как работает под капотом
Промоделируем эту логику на Python и проверим обе ветки:
def branch(rax):
# cmp rax, 10 ; jg bolshe
if rax > 10: # ветка bolshe
rbx = 1
else: # ветка else
rbx = 0
return rbx
print("rax=15 ->", branch(15))
print("rax=5 ->", branch(5))
print("rax=10 ->", branch(10))Вывод:
rax=15 -> 1 rax=5 -> 0 rax=10 -> 0
Заметьте: при rax=10 результат 0, потому что jg прыгает только при строго большем. Выбор между jg и jge (больше или равно) — частая точка ошибок «на единицу».
Частые ошибки
- Забыть
jmpв конце ветки else. Тогда после else исполнение «провалится» в код ветки if. - Перепутать знаковые и беззнаковые переходы.
jg/jlдля знаковых,ja/jbдля беззнаковых. - Вставить команду, портящую флаги, между
cmpи переходом. Например,addперезапишет флаги, и переход сработает не так.
Итог
- Ветвление =
cmp(выставить флаги) + условный переход (прочитать флаги). je/jneдля равенства,jg/jlдля знаковых,ja/jbдля беззнаковых.- В конце одной ветки нужен
jmp, чтобы пропустить другую. - Граница условия (
jgпротивjge) — частый источник ошибок на единицу.
Проверьте себя
1. Из каких двух шагов состоит ветвление в ассемблере?
Amov и ret
Bcmp (выставить флаги) и условный переход (прочитать флаги)
Cpush и pop
Dadd и sub
2. Когда сработает je после cmp rax, rbx?
AЕсли rax больше rbx
BЕсли rax равно rbx (ZF=1)
CВсегда
DЕсли rax меньше rbx
3. Зачем в конце ветки else ставят jmp?
AЧтобы ускорить код
BЧтобы не «провалиться» в код ветки if
CЧтобы очистить флаги
DЭто необязательно и бесполезно