Вопрос C / C / Java: будет ли выражение, используемое в цикле for, вычисляться несколько раз?

#java #c #c

#java #c #c

Вопрос:

например, у нас есть код, подобный этому:

 for (i = 0; i < function();   i )
{
   // loop body;
}
  

будет ли функция () вычисляться для каждого цикла?

Комментарии:

1. Какой язык C / C / Java? Выберите одно.

Ответ №1:

ДА. Оно будет вычисляться на каждой итерации цикла. Это должно быть потому, что существует вероятность того, что функция может возвращать другое значение каждый раз, когда она вызывается. Кроме того, может возникнуть побочный эффект вызова функции (например, изменение значения переменной или запись файла). Компилятор не может предположить иное. Если такое поведение нежелательно, вы можете использовать этот синтаксис цикла for для выполнения только 1 вычисления:

 for (i = 0, len=function(); i < len;   i )
{
   // loop body;
}
  

Обновить:

Некоторые отмечают, что существуют определенные крайние случаи, когда компиляторы могут оптимизировать несколько вызовов: например, const или встроенные функции в C , которые не имеют побочных эффектов, или, возможно, когда JVM может сделать вывод, что функция повторяема и не имеет побочных эффектов. Но если вам нужна гарантия, а не полагаться на то, что мог бы сделать за вас компилятор, я все равно рекомендую синтаксис цикла for, упомянутый выше.

Комментарии:

1. В C неверно, если функция помечена как «const»; в этом случае компилятор может выполнить оптимизацию.

2. @Майкл Аарон Сафьян: Что, если функция имеет побочный эффект?

3. @Asaph, хорошая мысль. Даже функция с пометкой «const» может изменять глобальные переменные или печатать что-либо. Обычно компилятор не может или не должен этого делать, если вы не укажете ему выполнять агрессивную оптимизацию.

4. @Asaph Даже когда function() не имеет побочного эффекта, компилятор не уверен на 100%, что сможет выполнить оптимизацию: тело цикла может повлиять на результат function() вызова. Например. std::string s = "1234"; for(int i=0; i < s.length(); i) { s = "12"; } Только проанализировав оба function() элемента цикла и все, что вызывается из обоих, компилятор может это доказать. Это редкий случай, когда компилятор способен это сделать (поэтому я сомневаюсь, что компиляторы даже пытаются), поэтому, если это имеет значение, я предлагаю переписать его в оптимизированную версию самостоятельно.

5. @Sjoerd: если функция встроенная, то оптимизаторы «попробуют»: например, в C это идиоматично для кода for (I i = container.begin(); i != container.end(); i) ... , и поэтому реализации стандартных контейнеров обычно (например, все GCC) гарантируют, что end() есть встроенная ссылка на существующее значение (даже не требующее вычитания или сложения), чтобы гарантировать отсутствие снижения производительности.

Ответ №2:

Да, на всех трех языках.

Чтобы избежать этого, вы можете поместить результат function() в переменную:

 int funcVal = function();
for (i = 0; i < funcVal;   i) {
    // ...
}
  

Компилятор / JVM не может оптимизировать это для вас (за исключением некоторых тривиальных случаев), потому что function() может иметь побочный эффект, например, вывод на консоль.

Ответ №3:

Это зависит.

В большинстве случаев функция будет вызываться несколько раз.

Однако:

В C , если это очень простая функция и сделать ее «встроенной», то вполне возможно, что там действительно нет вызова функции.

И в Java, если это тоже просто, JIT виртуальной машины может оптимизировать его, чтобы уменьшить ненужные вызовы.

Комментарии:

1. Даже встроенная функция будет вызываться несколько раз. Оптимизировать можно только функцию, возвращающую константу.

2. @Vladimir: неверно: если функция возвращает переменную, и оптимизатор видит, что переменная не изменена в цикле, то вполне законно считывать ее в регистр процессора и постоянно проверять на соответствие этому. Это for (i = start; i != function(); i) ... точно эквивалентно end = function(); for (i = start; i != end; i) ... случаю.

3. Возможно, я забыл C за 10 лет работы с Java, но быстрый тест с G -O4 показал, что функция вызывается несколько раз, даже когда ее можно полностью оптимизировать.

Ответ №4:

Аналогично ответу @Cameron, вы можете написать

 for(int i = 0, funcVal = function(); i < funcVal;   i) {
    // ...
}
  

Как предполагает @RollingBoy, функция() может быть встроена в C (компилятором) и Java (JVM) и, возможно, оптимизирована, чтобы она не вычислялась более одного раза.

Комментарии:

1. Встроенное не означает «вызванное один раз». Опущен только фактический вызов / ret; тело по-прежнему выполняется несколько раз.

2. Если все, что оно делает, это поиск поля, например размера, это может каждый раз возвращать значение, которое не получается из памяти, и делать то же самое.