Да, некоторые компиляторы умнее других. Ваш первый пример достойного компилятора, оптимизированного или нет, увидит, что он ничего не делает, он может или не может генерировать код для него, и он может или не может предупредить вас, что ваш код ничего не делает.
Я видел компилятор, который оптимизировал множество строк кода в разных функциях, вызываемых в цикле. На самом деле я пытался провести сравнение компилятора с помощью рандомизатора lfsr, который вызывался неоднократно в цикле (цикл запускался жестко заданным числом раз). Один компилятор добросовестно компилировал (и оптимизировал) код, выполняя каждый функциональный шаг, другой компилятор выяснил, что я делал, и созданный ассемблер был эквивалентен ldr r0, # 0x12345, где 0x12345 был ответом, если вы вычислили все переменные как сколько раз цикл был запущен. Одна инструкция.
Как видно из одного из ваших других вопросов, вы боретесь с использованием volatile, а также с другими особенностями языка. Во втором вашем примере, с только видимостью этой функции, компилятор не знает, на что указывает место в памяти, это вполне может быть аппаратный регистр, который, как ожидается, изменится в какой-то момент, или место в памяти, разделяемое в каким-то другим способом прерыванием или другим потоком, который, как ожидается, изменится в какой-то момент. Без энергозависимого оптимизатор вполне может сделать что-то вроде этого:
ldr r0,[r1]
compare:
cmp r0,#0
bne compare
Именно урок, который я усвоил, когда узнал о летучем. Место в памяти было прочитано (один раз), а затем цикл ждал, пока это значение изменится, так же, как мой код «сказал» это сделать. Не то, что я «хотел», чтобы это делало (выход из цикла, когда регистр, на который указывает указатель, изменился).
Теперь, если бы вы сделали что-то вроде этого:
void test( bool* escape ) {
while ( *escape );
}
pretest() {
bool escape = false;
test(&escape);
}
Некоторые компиляторы будут добросовестно компилировать этот код, даже если он ничего не делает (кроме циклов записи тактов, которые могут быть именно такими, как хотелось). Некоторые компиляторы могут смотреть вне одной функции в другую и видеть, что while (* escape); никогда не бывает правдой И некоторые из них не будут помещать какой-либо код в pretest (), но по какой-то причине будут точно включать функцию test () в двоичный файл, даже если он никогда не вызывается никаким кодом. Некоторые компиляторы полностью удаляют test () и оставляют предварительный тест как простой возврат. Все это совершенно верно в этих нереальных образовательных примерах.
Суть в том, что ваши два примера совершенно разные, в первом случае все, что нужно знать компилятору, чтобы определить, что цикл while является nop, - это все. Во втором случае компилятор не имеет возможности узнать состояние escape или, если он когда-либо изменится, и должен добросовестно скомпилировать цикл while в исполняемые инструкции. Единственная возможность оптимизации заключается в том, читает ли она из памяти при каждом проходе цикла.
Если вы хотите действительно понять, что происходит, скомпилируйте эти функции с различными вариантами оптимизации, а затем разберите объектный код.