К исходному языку это имеет ровно то отношение, что
наблюдаемый результат выполнения машинных команд будет в точности такой же, как и при выполнении инструкций исходного языка в том порядке, как они написаны (про методы параллельного программирования забудем). "А если не видно разницы - зачем платить больше?"(с)реклама. Так что всегда можно проигнорировать наличие машинных команд и оптимизатора и считать что выполняется прямо код исходного языка. В терминах которого и используется control flow для пояснения что и в каком порядке выполняется. И существование более низких уровней (компиляция и машинные команды и оптимизация) для понимания ничего не меняют.
Ещё раз подчеркну что именно
наблюдаемый результат: из-за этого добавление отладочного кода (например вывода значений переменных куда-то), даже вроде бы никак не влияющего на основной алгоритм работы, может кардинально изменить скомпилированный код (порядок и состав машинных команд) при включенной оптимизации.
PS. Если погрузиться ещё на уровень глубже, то и машинные команды выполняются современным (с технологией out-of-order) процессором не в том порядке как записаны в исполняемом файле. А ещё уровнем ниже и микрокоманды тоже выполняются в другом порядке (и вообще параллельно). И однако
наблюдаемый результат всегда (про исключения не буду) идентичен строго последовательному выполнению машинных команд из исполняемого файла.
PPS. Хоть и офтопик, но добавлю: надо не забывать что UB (undefined behavior) означает именно
произвольное поведение, в том числе и
нормальное (ожидаемое программистом). И если два запуска одной и той же программы с идентичными данными вдруг выдают разное поведение при наличии в коде UB - то это именно не различное поведение, а
одинаковое (произвольное)! Частая ошибка считать, что раз программа отработала правильно (как ожидал программист), то она правильная - при наличии в коде UB это не так, она неправильная, но
случайно отработала ожидаемо/правильно. Но нет никакой гарантии что в следующий раз будет так же.
-- 26.12.2019, 11:12 --Поток управления (control flow) - обычно просто порядок, в котором выполняются инструкции исходного языка. Не как они записаны, а как будут выполняться. Используется часто для объяснения почему не все записанные в коде операторы будут реально выполняться именно в порядке записи исходного текста.
Простые примеры:
int a=3;
if (b>0) {
int c=5;
cout << c*a+b;
}
cout << c;
Объявление переменной
c происходит внутри условного оператора и снаружи её не видно, хотя вот же код написан и даже порядок выполнения не нарушается, казалось бы можно и дальше ею пользоваться, однако обращение к ней в последней строке - ошибка (если она не была объявлена и инициализирована где-то выше показанного куска кода).
int b;
for (int a=1; a<3; a++) {
if (a==2) {
cout << a+7; b=13;
}
if (a==1) {
cout << a*b;
}
}
Это пример что порядок выполнения инструкций (control flow) не всегда ровно в порядке написания: при первом проходе условных операторов выполнится второй из них, а при втором проходе - первый. В данном случае control flow будет в порядке:
int; for; if(a==2); if(a==1); cout<<a*b; for; if(a==2); cout<<a+7; b=13; if(a==1); for;, хотя записаны они в другом порядке. И обращение к переменной
b во втором if - ошибка, потому что она к этому моменту ещё не инициализирована. Именно из-за отличия control flow (порядка выполнения) от порядка записи в файле.