Подтвердить что ты не робот

Технические причины форматирования при увеличении на 1 в цикле "for"?

Всюду по сети образцы кода имеют циклы for, которые выглядят следующим образом:

for(int i = 0; i < 5; i++)

в то время как я использовал следующий формат:

for(int i = 0; i != 5; ++i)

Я делаю это, потому что считаю, что это более эффективно, но действительно ли это имеет значение в большинстве случаев?

4b9b3361

Ответ 1

Я решил перечислить наиболее информативные ответы, поскольку этот вопрос становится немного переполненным.

DenverCoder8 для настольной маркировки явно заслуживает некоторого признания, а также скомпилированные версии циклов Лукас. Тим Джи показал различия между до и после приращения, в то время как User377178 выделил некоторые из плюсов и минусов < и! =. Tenacious Techhunter написал о оптимизации циклов в целом и заслуживает упоминания.

Там у вас есть мои лучшие 5 ответов.

Ответ 2

Все любят свои микро-оптимизации, но это не повлияет, насколько я могу судить. Я скомпилировал два варианта с g++ для процессоров Intel без каких-либо причудливых оптимизаций, и результаты для

для (int я = 0; я < 5; я ++):

    movl $0, -12(%ebp)
    jmp L2
L3:
    leal    -12(%ebp), %eax
    incl    (%eax)
L2:
    cmpl    $4, -12(%ebp)
    jle L3

for (int я = 0; я!= 5; ++ i)

    movl    $0, -12(%ebp)
    jmp L7
L8:
    leal    -12(%ebp), %eax
    incl    (%eax)
L7:
    cmpl    $5, -12(%ebp)
    jne L8

Я думаю, что "jle" и "jne" должны переводить на одинаковые быстрые инструкции для большинства архитектур. Поэтому для производительности вы не должны различать их. В общем, я бы согласился с тем, что первый из них немного безопаснее, и я также считаю более распространенным.

EDIT (2 года спустя):. Поскольку эта тема недавно привлекла много внимания, я хотел бы добавить, что будет сложно ответить на этот вопрос в целом. Какие версии кода более эффективны конкретно не определены C-Standard [PDF] (и то же самое относится к С++ и, возможно, также для С#).

Раздел 5.1.2.3 Выполнение программы

§1 Семантические описания в этом международном стандарте описывают поведение абстрактная машина, в которой вопросы оптимизации не имеют значения.

Но разумно предположить, что современный компилятор будет создавать одинаково эффективный код, и я думаю, что в очень редких случаях "loop-test" и "counting expression" будут узким местом для цикла.

Что касается вкуса, пишу for(int i = 0; i < 5; ++i).

Ответ 3

Если по какой-либо причине i перескакивает до 50 в цикле, ваша версия будет зацикливаться навсегда. i < 5 - проверка работоспособности.

Ответ 4

Форма

for (int i = 0; i < 5; i++)

является идиоматичным, поэтому его легче читать для опытных программистов на С. Особенно при использовании для итерации по массиву. Вы должны писать идиоматический код, когда это возможно, когда он читает быстрее.

Это также немного безопаснее в ситуациях, когда вы меняете я внутри цикла или используете приращение, отличное от 1. Но это незначительная вещь. Лучше всего тщательно спроектировать свою петлю и добавить некоторые утверждения, чтобы рано ломать ломаные предположения.

Ответ 5

Если правило приращения немного изменится, вы сразу получите бесконечный цикл. Я предпочитаю первое условие окончания.

Ответ 6

Это зависит от языка.

Тексты на С++ часто предлагают второй формат, так как это будет работать с итераторами, которые можно сравнивать (! =) напрямую, но не с условием "больше или меньше". Кроме того, pre increment может быть быстрее, чем post increment, так как нет необходимости в копировании переменной для сравнения - однако оптимизаторы могут справиться с этим.

Для целых чисел или форм работает. Общая идиома для C является первой, тогда как для С++ она является второй.

Для использования С# и Java я хотел бы зациклиться на всех вещах.

В С++ существует также функция std:: for_each, требующая использования функтора, который для простых случаев, вероятно, более сложный, чем любой пример здесь, и Boost FOR_EACH, который может выглядеть как С# foreach, но является сложным внутри.

Ответ 7

Что касается использования ++ я вместо я ++, это не влияет на большинство компиляторов, однако ++ я мог бы быть более эффективным, чем я ++ при использовании в качестве итератора.

Ответ 8

На самом деле есть четыре перестановки в том, что вы даете. К вашим двум:

for(int i = 0; i < 5; i++)
for(int i = 0; i != 5; ++i)

Можно добавить:

for(int i = 0; i < 5; ++i)
for(int i = 0; i != 5; i++)

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

В некоторых случаях это может иметь определенный смысл для конкретного ума с конкретным случаем, чтобы думать о "меньше" или "не равно" в зависимости от причины, по которой мы выбрали 0 и 5, но даже тогда, очевидный для одного кодера, может быть не с другим.

Более абстрактно, они имеют следующие формы:

for(someType i = start; i < end; i++)
for(someType i = start; i != end; ++i)
for(someType i = start; i < end; ++i)
for(someType i = start; i != end; i++)

Очевидное различие заключается в том, что в двух случаях someType должен иметь значение для <, а для остальных он должен иметь значение для !=. Типы, для которых != определены и < не являются довольно распространенными, включая довольно много объектов итератора в С++ (и, возможно, на С#, где возможен такой же подход, как и итераторы STL, а иногда и полезный, но ни как идиоматический, напрямую поддерживаемый обычными библиотеками, и не так часто полезен, поскольку существуют конкурирующие идиомы с более прямой поддержкой). Стоит отметить, что подход STL специально разработан так, чтобы включать указатели в набор допустимых типов итераторов. Если вы привыкли использовать STL, вы будете рассматривать формы с != гораздо более идиоматичными, даже если они применяются к целым числам. Лично очень незначительное количество воздействия на него было достаточно, чтобы сделать его моим инстинктом.

С другой стороны, хотя определение < и not != будет более редким, оно применимо к случаям, когда либо мы заменяем приращение другим увеличением значения i, либо где i может быть изменено внутри цикла.

Итак, существуют определенные случаи с обеих сторон, где один или другой - единственный подход.

Теперь для ++i vs i++. Опять же, с целыми числами и при вызове напрямую, а не через функцию, возвращающую результат (и шансы даже тогда), практический результат будет точно таким же.

В некоторых языках C-стиля (без перезагрузки оператора) целые числа и указатели - это единственные случаи. Мы могли бы просто искусственно изобрести случай, когда инкремент вызывается через функцию только для изменения того, как это происходит, и, скорее всего, компилятор все равно превратит их в одну и ту же вещь.

С++ и С# позволяют нам переопределять их. Обычно префикс ++ работает как функция, которая делает:

val = OneMoreThan(val);//whatever OneMoreThan means in the context.
//note that we assigned something back to val here.
return val;

И постфикс ++ работает как функция, которая делает:

SomeType copy = Clone(val);
val = OneMoreThan(val);
return copy;

Ни С++, ни С# не соответствуют вышеизложенному (я совершенно сознательно не сделал ни одного псевдокода), но в любом случае может быть сделана копия или, возможно, две. Это может быть или не быть дорогостоящим. Его можно или не следует избегать (в С++ мы часто можем полностью его избежать для формы префикса, возвращая this и в postfix, возвращая void). Это может быть или не быть оптимизировано ни к чему, но остается, что в некоторых случаях было бы более эффективно выполнять ++i, чем i++.

В частности, есть небольшая возможность небольшого улучшения производительности с помощью ++i, а с большим классом она может быть даже значительной, но не позволяет кому-то переопределить на С++, так что они имеют совершенно разные значения (довольно плохая идея), это вообще не возможно для того, чтобы это было наоборот. Таким образом, привыкание использовать префикс поверх постфикса означает, что вы можете получить улучшение, возможно, один раз в тысячу, но не проиграете, поэтому в него часто входят привычные кодеры С++.

Таким образом, нет абсолютно никакой разницы в двух случаях, указанных в вашем вопросе, но они могут быть в вариантах того же самого.

Ответ 9

Я переключился на использование != около 20+ лет назад после чтения книги Дейкстры, названной "Дисциплина программирования" . В своей книге Дейкстра заметил, что более слабые условия продолжения приводят к более сильным пост-условиям в конструкциях цикла.

Например, если мы изменим вашу конструкцию, чтобы выставить i после цикла, пост-условие петли первого типа будет i >= 5, а пост-условие второго цикла будет намного сильнее i == 5, Это лучше для рассуждений о программе в формальных терминах инвариантов цикла, пост-условий и слабых предварительных условий.

Ответ 10

Я согласен с тем, что было сказано о удобочитаемости - важно иметь код, который легко прочитать для сопровождающего, хотя вы бы надеялись, что тот, кто это понимает, будет понимать как предварительные, так и последующие приращения.

Тем не менее, я подумал, что я проведу простой тест и получу твердые данные о том, какая из четырех циклов работает быстрее всего. Я нахожусь на среднем компьютере спецификации, компилируя с javac 1.7.0.

Моя программа создает цикл for, итерируя 2 000 000 раз по всему (чтобы не загружать интересные данные с тем, сколько времени требуется, чтобы делать все, что находится в цикле for). Он использует все четыре типа, предложенные выше, и умножает результаты, повторяя 1000 раз, чтобы получить среднее значение.

Фактический код:

public class EfficiencyTest
{
public static int iterations = 1000;

public static long postIncLessThan() {
    long startTime = 0;
    long endTime = 0;
    startTime = System.nanoTime();
    for (int i=0; i < 2000000; i++) {}
    endTime = System.nanoTime();
    return endTime - startTime;
}

public static long postIncNotEqual() {
    long startTime = 0;
    long endTime = 0;
    startTime = System.nanoTime();
    for (int i=0; i != 2000000; i++) {}
    endTime = System.nanoTime();
    return endTime - startTime;
}

public static long preIncLessThan() {
    long startTime = 0;
    long endTime = 0;
    startTime = System.nanoTime();
    for (int i=0; i < 2000000; ++i) {}
    endTime = System.nanoTime();
    return endTime - startTime;
}

public static long preIncNotEqual() {
    long startTime = 0;
    long endTime = 0;
    startTime = System.nanoTime();
    for (int i=0; i != 2000000; ++i) {}
    endTime = System.nanoTime();
    return endTime - startTime;
}

public static void analyseResults(long[] data) {
    long max = 0;
    long min = Long.MAX_VALUE;
    long total = 0;
    for (int i=0; i<iterations; i++) {
        max = (max > data[i]) ? max : data[i];
        min = (data[i] > min) ? min : data[i];
        total += data[i];
    }
    long average = total/iterations;

    System.out.print("max: " + (max) + "ns, min: " + (min) + "ns");
    System.out.println("\tAverage: " + (average) + "ns");
}

public static void main(String[] args) {
    long[] postIncLessThanResults = new long [iterations];
    long[] postIncNotEqualResults = new long [iterations];
    long[] preIncLessThanResults = new long [iterations];
    long[] preIncNotEqualResults = new long [iterations];

    for (int i=0; i<iterations; i++) {
        postIncLessThanResults[i] = postIncLessThan();
        postIncNotEqualResults[i] = postIncNotEqual();
        preIncLessThanResults[i] = preIncLessThan();
        preIncNotEqualResults[i] = preIncNotEqual();
    }
    System.out.println("Post increment, less than test");
    analyseResults(postIncLessThanResults);

    System.out.println("Post increment, inequality test");
    analyseResults(postIncNotEqualResults);

    System.out.println("Pre increment, less than test");
    analyseResults(preIncLessThanResults);

    System.out.println("Pre increment, inequality test");
    analyseResults(preIncNotEqualResults);
    }
}

Извините, если я скопировал это неправильно!

Результаты подтвердили меня - тестирование i < maxValue заняло около 1,39 мс за цикл, независимо от того, используют ли они пре-или пост-приращения, но i != maxValue занял 1,05 мс. Это a либо 24,5% экономии, либо 32,5% потери времени, в зависимости от того, как вы на это смотрите.

Конечно, сколько времени займет цикл for, возможно, это не ваше узкое место, но это тот вид оптимизации, о котором это полезно знать, в редкий случай, когда вам это нужно.

Я думаю, что по-прежнему буду тестировать меньше, чем!

Edit

Я также тестировал декремент i, и обнаружил, что это действительно не влияет на время, которое требуется - for (int i = 2000000; i != 0; i--) и for (int i = 0; i != 2000000; i++) оба имеют одинаковый период времени, например for (int i = 2000000; i > 0; i--) и for (int i = 0; i < 2000000; i++).

Ответ 11

Я бы никогда этого не сделал:

for(int i = 0; i != 5; ++i)

i!= 5 оставляет его открытым для возможности, что я никогда не будет 5. Слишком легко пропустить его и запустить в бесконечный цикл или ошибку доступа к массиву.

++i

Хотя многие знают, что вы можете поставить ++ впереди, есть много людей, которые этого не делают. Код должен быть читаемым для людей, и хотя это может быть микро-оптимизация, чтобы сделать код быстрее, на самом деле не стоит лишней головной боли, когда кто-то должен изменить код и понять, почему это было сделано.

Я думаю, что у Дугласа Крокфорда есть лучшее предложение, и это значит, что он не использует ++ или - вообще. Он может просто стать слишком запутанным (может быть, не в цикле, но определенно в других местах), и так же легко писать я = я + 1. Он думает, что это просто плохая привычка выбраться, и я вроде как согласитесь, увидев какой-то ужасный "оптимизированный" код.

Я думаю, что crockford становится с этими операторами, вы можете заставить людей писать такие вещи, как:

var x = 0;
var y = x++;

y = ++x * (Math.pow(++y, 2) * 3) * ++x;

alert(x * y);

//ответ - 54 бит.

Ответ 12

В родовом коде вы должны предпочесть версию с оператором !=, поскольку для этого требуется, чтобы ваш i был одинаково сопоставим, а версия < требует, чтобы он был сравнительным с точки зрения сопоставимости. Последнее является более сильным требованием, чем первое. Обычно вам следует избегать более сильных требований, когда более слабое требование является вполне достаточным.

Сказав, что в вашем конкретном случае, если int i оба будут работать одинаково хорошо, и не будет никакой разницы в производительности.

Ответ 13

В этих случаях не стоит заботиться об эффективности, потому что ваш компилятор обычно достаточно умный, чтобы оптимизировать ваш код, когда он способен.

Я работал в компании, которая выпускает программное обеспечение для критически важных систем, и одним из правил было то, что цикл должен заканчиваться символом "<" вместо a! =. Для этого есть несколько веских причин:

  • Ваша управляющая переменная может перейти к более высокому значению некоторой проблемой hw или некоторым вторжением памяти;

  • При обслуживании можно увеличить значение итератора внутри цикла или сделать что-то вроде "i + = 2", и это заставит ваш цикл катиться навсегда;

  • Если по какой-то причине ваш тип итератора изменяется с "int" на "float" (я не знаю, почему кто-то это сделал, но в любом случае...) точное сравнение для точек с плавающей точкой - это плохая практика.

(Стандарт кодирования MISRA С++ (для критически важных для безопасности систем) также говорит вам предпочесть "<" вместо "! =" в правиле 6-5-2. Я не знаю, могу ли я опубликовать определение правила здесь, потому что MISRA - платный документ.)

О ++ я или я ++, я бы предпочел использовать ++ i. Для вас нет никакой разницы, когда вы работаете с базовыми типами, но когда вы используете итератор STL, преинкремент более эффективен. Поэтому я всегда использую preincrement, чтобы привыкнуть к нему.

Ответ 14

Для записи коболь-эквивалент цикла "for": -

    PERFORM VARYING VAR1 
            FROM +1 BY +1
            UNTIL VAR1 > +100
  *      SOME VERBOSE COBOL STATEMENTS HERE
    END-PERFORM.

или

PERFORM ANOTHER-PARAGRAPH
        VARYING VAR2 BY +1 
        UNTIL TERMINATING-CONDITION
        WITH TEST AFTER.

В этом есть много вариаций. Основной способ для людей, чьи умы не были повреждены при длительном воздействии COBOL, - это по умолчанию UNTIL на самом деле означает WHILE, т.е. Тест выполняется в начале цикла, прежде чем переменная цикла будет увеличена и до обрабатывается тело цикла. Вам нужно "WITH TEST AFTER", чтобы сделать его правильным UNTIL.

Ответ 15

Вторая менее читаема, я думаю (хотя бы потому, что "стандартная" практика кажется первой).

Ответ 16

Числовые литералы, посыпанные вашим кодом? Для стыда...

Возвращаясь назад, Дональд Кнут однажды сказал

Мы должны забыть о небольших эффективности, скажем, около 97% время: преждевременная оптимизация - это корень всего зла.

Итак, это действительно сводится к тому, что проще разбирать

Итак... принимая во внимание оба вышеизложенного, какой из следующих алгоритмов проще разбирать?

for (int i = 0; i < myArray.Length; ++i)

for (int i = 0; i != myArray.Length; ++i)

Изменить: я знаю, что массивы в С# реализуют интерфейс System.Collections.IList, но это не обязательно верно на других языках.

Ответ 17

Что касается читаемости. Будучи программистом на С#, который любит Ruby, я недавно написал метод расширения для int, который позволяет использовать следующий синтаксис (как в Ruby):

4.Times(x => MyAction(x));

Ответ 18

Подводя итоги за и против обоих вариантов

Плюсы!=

  • Когда int заменяется некоторым итератором или типом, переданным через аргумент шаблона, есть больше шансов, что он будет работать, он будет делать то, что ожидается, и будет более эффективным.
  • будет "зацикливаться навсегда", если произойдет что-то непредвиденное с переменной i, позволяющей обнаружение ошибок

Плюсы <

  • как говорят другие, так же эффективны, как и другие с простыми типами
  • он не будет запускаться "навсегда", если я увеличивается в цикле или 5 заменяется некоторым выражением, которое изменяется при запуске цикла
  • будет работать с типом float
  • более читаемый - вопрос привыкания к

Мои выводы:

  • Возможно, версия != должна использоваться в большинстве случаев, когда я является дискретной, и она также, как и другая сторона сравнения, не предназначена для подделки внутри петля.

  • Хотя наличие < будет явным признаком того, что я имеет простой тип (или оценивается простым типом) и, условие не является прямо: я или условие дополнительно модифицируется внутри цикла и/или параллельной обработки.

Ответ 19

Похоже, никто не указал причину, по которой исторически оператор preincrement ++i был предпочтительнее постфикса i++ для небольших циклов.

Рассмотрим типичную реализацию префикса (приращение и выборку) и постфикс (выборка и приращение):

// prefix form: increment and fetch
UPInt& UPInt::operator++()
{
   *this += 1;      // increment
   return *this;    // fetch
}

// posfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
   const UPInt oldValue = *this;
   ++(*this);
   return oldValue;
} 

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

int a = 0;
int b = a++; // b = 0, the old value, a = 1 

В небольшом цикле это дополнительное выделение, требуемое postfix, теоретически может сделать его более медленным, и поэтому старая школьная логика является префиксом более эффективной. Таким образом, многие программисты на C/С++ придерживались привычки использовать префиксную форму.

Однако в другом месте отмечается, что современные компиляторы являются умными. Они замечают, что при использовании постфиксной формы в цикле for, возвращаемое значение постфикса не требуется. Таким образом, нет необходимости отслеживать старое значение и его можно оптимизировать - оставляя тот же машинный код, который вы получили бы из формы префикса.

Ответ 20

Ну... это прекрасно, если вы не изменяете i внутри цикла for. Настоящий синтаксис "BEST" полностью зависит от вашего желаемого результата.

Ответ 21

Если ваш индекс не был int, но вместо этого (скажем) С++-класс, то второй пример мог бы быть более эффективным.

Однако, как написано, ваша уверенность в том, что вторая форма более эффективна, просто неверна. Любой достойный компилятор будет иметь отличные идиомы для простого цикла и будет производить высококачественный код для любого примера. Подробнее:

  • В цикле for, который делает тяжелые вычисления с критичной производительностью, арифметика индекса будет почти незначительной частью общей нагрузки.

  • Если ваш цикл for является критичным по производительности и не делает тяжелых вычислений таким образом, что на самом деле имеет значение арифметика индекса, вы почти наверняка будете реструктурировать свой код, чтобы делать больше работы в каждом проходе цикла.

Ответ 22

Когда я впервые начал программирование на C, я использовал форму ++i для циклов, просто потому, что компилятор C, который я использовал в то время, не делал много оптимизации и в этом случае создавал бы более эффективный код.

Теперь я использую форму ++i, потому что она читается как "increment i", тогда как i++ читается как "i увеличивается", и любой преподаватель английского языка будет предупреждать вас о том, чтобы избежать пассивного голоса.

Нижняя строка - это то, что вам кажется более читаемым.

Ответ 23

Я думаю, что в конце концов это сводится к личным предпочтениям.
Мне нравится идея

for(int i = 0; i < 5; i++)

над

for(int i = 0; i != 5; ++i)

из-за того, что по какой-то причине существует вероятность того, что я прыгаю мимо 5. Я знаю, что в большинстве случаев шансы на это случаются, но я думаю, что в конце концов это хорошая практика.

Ответ 24

Мы можем использовать еще один трюк для этого.

для (i = 5; i > 0; я -)

Я полагаю, что большинство компиляторов оптимизируют такие петли. Я не уверен. Кто-то, пожалуйста, подтвердите.

Ответ 25

В конечном счете решающим фактором в отношении того, что является более эффективным, является ни язык, ни компилятор, а скорее базовое оборудование. Если вы пишете код для встроенного микроконтроллера, такого как 8051, подсчитывая против подсчета вниз, больше или меньше, чем против, а не равно, и увеличивая против декремента, может изменить производительность, в пределах очень ограниченная шкала времени ваших циклов.

Хотя достаточная поддержка языка и компилятора может (и часто делает) уменьшать отсутствие инструкций, необходимых для реализации указанного кода, оптимальным, но концептуально эквивалентным образом, кодирование самого аппаратного обеспечения гарантирует производительность, а не просто надежду на адекватную оптимизацию во время компиляции.

И все это означает, что нет единого универсального ответа на ваш вопрос, так как существует множество различных микроконтроллеров низкого уровня.

Тем не менее,

намного, чем оптимизация того, как ваши итерации цикла, петли и перерывы, изменяет то, что он делает на каждой итерации. При вызове цикла for одна дополнительная инструкция сохраняет две или более инструкции на каждой итерации, делает это! Вы получите чистую прибыль за один или несколько циклов! Для действительно оптимального кода вы должны взвесить последствия полной оптимизации того, как цикл for выполняет итерацию над тем, что происходит на каждой итерации.

Все, что сказано, хорошее эмпирическое правило состоит в том, что если вам будет сложно запомнить все инструкции по сборке для вашего конкретного целевого оборудования, оптимальные инструкции по сборке для всех вариантов цикла "for", вероятно, были полностью учтено. Вы всегда можете проверить, действительно ли вы ДЕЙСТВИТЕЛЬНО.

Ответ 26

Я вижу множество ответов, используя выделенный код, и целое число. Однако вопрос был специфическим для "для циклов", а не для конкретного, упомянутого в исходном сообщении.

Я предпочитаю использовать оператор приращения/уменьшения префиксов, поскольку он в значительной степени гарантированно работает так же быстро, как и постфиксный оператор, но имеет возможность быть более быстрым при использовании с не-примитивными типами. Для таких типов, как целые, это никогда не имеет значения с любым современным компилятором, но если вы привыкнете использовать префиксный оператор, то в тех случаях, когда он будет обеспечивать ускорение скорости, вы получите от него выгоду.

Недавно я запустил инструмент статического анализа для большого проекта (возможно, около 1-2 миллионов строк кода), и он обнаружил около 80 случаев, когда постфикс использовался в случае, когда префикс обеспечивал бы преимущество в скорости. В большинстве из этих случаев преимущество было небольшим, поскольку размер контейнера или количество циклов обычно были небольшими, но в других случаях он мог бы производить итерацию более 500 предметов.

В зависимости от типа возрастающего/уменьшающегося объекта, когда возникает постфикс, также может произойти копия. Мне было бы интересно узнать, сколько компиляторов обнаружит случай, когда постфикс используется, когда его значение не ссылается, и, таким образом, копия не может быть использована. Будет ли он генерировать код в этом случае для префикса? Даже инструмент статического анализа упомянул, что некоторые из этих 80 случаев, которые он нашел, могут быть оптимизированы в любом случае, но зачем брать шанс и позволить компилятору решить? Я не считаю, что префиксный оператор путается при использовании в одиночку, он становится только бременем для чтения, когда он начинает привыкать, inline, как часть логического оператора:

int i = 5;
i = ++i * 3;

Если подумать о приоритете оператора, не нужно будет с помощью простой логики.

int i = 5;
i++;
i *= 3;

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

int i = 5;
++i;
i *= 3;

Как я уже сказал, это не имеет большого значения, но использование префикса, когда переменная не используется иначе, в том же самом заявлении - это просто хорошая привычка, на мой взгляд, потому что в какой-то момент вы будете использовать его в не примитивном классе, и вы можете сэкономить себе операцию копирования.

Только мои два цента.

Ответ 27

На многих архитектурах гораздо проще проверить, не является ли что-то нулевым, будь то какое-то другое произвольное целое число, поэтому, если вы действительно хотите оптимизировать выдержку из чего-либо, по возможности отсчитывайте, а не вверх (здесь a пример на чипах ARM).

В целом, это действительно зависит от того, как вы думаете о числах и подсчете. Я занимаюсь множеством DSP и математики, поэтому для меня более естественным считается отсчет от 0 до N-1. В этом отношении вы можете быть разными.

Ответ 28

Я помню один сегмент кода, где я получал приращение на 2 вместо 1 из-за некоторой ошибки, и это вызывало его в бесконечном цикле. Поэтому лучше иметь этот цикл, как показано в первом варианте. Это также более читаемо. Поскольку я!= 5 и я < 5 передает читателю два разных значения. Также, если вы увеличиваете переменную цикла, тогда я < 5, как предполагается, заканчивает некоторый момент времени, в то время как я!= 5 никогда не может закончиться из-за некоторой ошибки.

Ответ 29

Цикл FORTRAN DO и цикл BASIC FOR реализованы < (фактически <=) для положительных приращений. Не уверен, что сделал COBOL, но я подозреваю, что это было похоже. Таким образом, этот подход был "естественным" для дизайнеров и пользователей "новых" языков, таких как C.

Кроме того, < более вероятен, чем !=, чтобы завершаться в ошибочных ситуациях и одинаково справедлив для значений целых чисел и значений с плавающей запятой.

Первый пункт выше - вероятная причина, по которой стиль начался, второй - основной причиной его продолжения.

Ответ 30

Это нехороший подход к использованию как!= 5. Но

for (int i =0; i<index; ++i)

более эффективен, чем

for(int i=0; i<index; i++)

Потому что я ++ сначала выполняет операцию копирования. Для получения подробной информации вы можете посмотреть перегрузку оператора на С++.