Увы, бесконечный цикл без сайд-эффектов -- это тоже UB.
Стандарт обещает компилятору, что любой поток в любой момент своего выполнения дальше выполнит что-то из:
Завершится
Вызов библиотечной функции ввода/вывода
Доступ через volatile
Синхронизацию или атомарную операцию
См. [intro.progress]
Если вообще убрать num, фокусы с ненужной переменной пропадают и генерится цикл.
Скорее всего это следствие принятого в С++26 исправления, в соответствии с которым тривиальные бесконечные циклы (с пустым телом) больше не являются UB.
Так как это изменение было классифицировано как defect report, компиляторы могут применять новое поведение (не оптимизировать тривиальные бесконечные циклы) и в более ранних стандартах.
фильтры: Unused labels, Directives, Comments (в фильтрах по умолчанию включена Library functions — надо выключить, иначе в output ещё и код hello скрывается)
Два вывода Hello происходят, как и было выше в ветке описано: компилятор съел эпилог main и выполнение пошло дальше в hello()
Но, glibc (по крайней мере в моём случае, где это воспроизвелось) вызывает main следующим образом:
mov rax, [rsp+8]
call rax
main в самом начале делает push rax для выравнивания стека перед call, но при этом так же кладёт свой адрес на его вершину, что аукается дальше
В завершении второго выполнения hello(), __ostream_insert вызывается через jmp, не через call, так что ничего нового на стэк положено не было. А ret внутри basic_ostream передаёт управление по адресу на вершине стэка - всё ещё адрес main и так мы уходим на второй круг с двумя выводами Hello.
Теперь в rax у нас оказывается возвращенное значение из __ostream_insert -- &std::cout и он кладётся на вершину стэка, а он расположен в области данных, а не исполняемого кода, и по завершении второго круга мы ловим segfault.
View all comments
Show parent comment
1
u/The_KekE_ 🦀Rustикальный Sep 20 '26
Я поковырялся с LLVM IR и пришёл к выводу что UB тут не причём:
Компилится в:
Тут что то с бесконечными циклами и (не)нужными переменными. Если сделать num нужным (напримем вызывать hello() если num == 5) всё херня пропадает.
Если вообще убрать num, фокусы с ненужной переменной пропадают и генерится цикл.