Есть ли разница в производительности между i ++ и ++ i в C?

Есть ли разница в производительности между i++и ++iесли полученное значение не используется?

24.08.2008 06:48:23
Расслабьтесь только потому, что вы не понимаете, что обоснование не делает обоснование смешным или бессмысленным. Люди проголосовали за этот вопрос, потому что понимание ответа помогло им лучше понять кое-что о языке Си. Ответ также показывает интересный способ продемонстрировать, что два фрагмента кода семантически идентичны.
Mark Harrison 6.10.2010 21:18:38
Связанный, тот же вопрос, но явно для C ++: stackoverflow.com/questions/24901/…
FluxLemur 10.08.2019 23:33:52
14 ОТВЕТОВ
РЕШЕНИЕ

Резюме: Нет.

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

Мы можем продемонстрировать это, посмотрев на код этой функции, как с, так ++iи i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

Файлы одинаковые, за исключением ++iи i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Мы скомпилируем их, а также получим сгенерированный ассемблер:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

И мы видим, что и сгенерированный объект и файлы ассемблера одинаковы.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
391
6.10.2015 19:08:24
Я знаю, что этот вопрос касается C, но мне было бы интересно узнать, могут ли браузеры выполнять эту оптимизацию для javascript.
TM. 12.08.2009 21:58:01
Таким образом, «Нет» верно для одного компилятора, с которым вы тестировали.
Andreas 25.01.2011 08:59:38
@ Андреас: Хороший вопрос. Я был автором компилятора и имел возможность протестировать этот код на многих процессорах, операционных системах и компиляторах. Единственный найденный мной компилятор, который не оптимизировал случай i ++ (фактически, компилятор, который профессионально привлек к этому мое внимание), был компилятором Software Toolworks C80 Уолта Билофски. Этот компилятор был для систем Intel 8080 CP / M. Можно с уверенностью сказать, что любой компилятор, который не включает эту оптимизацию, не предназначен для общего использования.
Mark Harrison 25.01.2011 20:55:48
Несмотря на то, что разница в производительности незначительна и оптимизирована во многих случаях, обратите внимание, что все же рекомендуется использовать ее ++iвместо i++. Нет абсолютно никаких причин, и если ваше программное обеспечение когда-либо пройдет через цепочку инструментов, которая не оптимизирует его, ваше программное обеспечение будет более эффективным. Учитывая, что набирать текст так же просто, ++iкак и печатать i++, на самом деле нет никаких оснований не использовать его ++iв первую очередь.
monokrome 5.07.2012 17:45:05
@monokrome Поскольку разработчики могут предоставить собственную реализацию для операторов префикса и постфикса на многих языках, это не всегда может быть приемлемым решением для компилятора без предварительного сравнения этих функций, что может быть нетривиальным.
pickypg 20.09.2012 00:50:19

Мой C немного ржавый, поэтому заранее прошу прощения. По скорости я могу понять результаты. Но я не совсем понимаю, как оба файла оказались в одном хеше MD5. Может быть, цикл for работает так же, но не будут ли следующие 2 строки кода генерировать разные сборки?

myArray[i++] = "hello";

против

myArray[++i] = "hello";

Первый записывает значение в массив, затем увеличивает i. Второе увеличение i затем записывает в массив. Я не эксперт по сборке, но я просто не вижу, как один и тот же исполняемый файл будет сгенерирован этими двумя разными строками кода.

Просто мои два цента.

0
24.08.2008 14:22:47
@Jason Z Оптимизация компилятора происходит до того, как сборка будет завершена, будет видно, что переменная i больше нигде не используется в той же строке, поэтому удержание ее значения будет пустой тратой, и, вероятно, она перевернет ее на i ++. Но это только предположение. Я не могу дождаться, пока один из моих лекторов попытается сказать, что это быстрее, и я стану тем парнем, который исправляет теорию с помощью практических доказательств. Я уже почти чувствую вражду ^ _ ^
Tarks 24.08.2008 14:35:34
«Мой C немного ржавый, поэтому я прошу прощения заранее». Ничего плохого в вашем C, но вы не прочитали оригинальный вопрос полностью: «Есть ли разница в производительности между i ++ и ++ i, если полученное значение не используется? » Обратите внимание на курсив. В вашем примере, «результат» из я ++ / ++ я это используется, но в идиоматическом для цикла, «результат» предсессионного / postincrement оператора не используется , так что компилятор может делать то , что ему нравится.
Roddy 1.09.2008 16:22:50
в вашем примере код будет другим, потому что вы используете значение. пример, в котором они были одинаковыми, использовал только ++ для приращения, и не использовал значение, возвращаемое либо.
John Gardner 20.09.2008 05:15:10
Исправлено: «компилятор видел бы, что возвращаемое значение i ++ не используется, поэтому он переключил бы его на ++ i». То, что вы написали, неверно еще и потому, что вы не можете иметь i вместе с одним из i ++, i ++ в одной строке (оператор), этот результат не определен.
Blaisorblade 15.01.2009 00:18:57
Изменение foo[i++]к foo[++i]ничего не меняя остальное, очевидно , изменить семантику программы, но на некоторых процессорах при использовании компилятора без оптимизации логики петли грузоподъемной, увеличивающихся pи qодин раз , а затем запустить цикл , который выполняет , например , *(p++)=*(q++);будет быстрее , чем с помощью цикла , который выполняет *(++pp)=*(++q);. Для очень трудных циклов на некоторых процессорах разница в скорости может быть значительной (более 10%), но это, вероятно, единственный случай в C, где постинкремент существенно быстрее, чем преинкремент.
supercat 8.09.2014 18:34:16

В C компилятор обычно может оптимизировать их, чтобы они были одинаковыми, если результат не используется.

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

5
24.08.2008 14:29:01

Изучение Скотта Мейерса, более эффективный c ++. Пункт 6: Различать префиксную и постфиксную формы операций увеличения и уменьшения .

Версия префикса всегда предпочтительнее постфикса в отношении объектов, особенно в отношении итераторов.

Причина этого, если вы посмотрите на схему звонков операторов.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

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

Вот почему, когда вы видите примеры с использованием итераторов, они всегда используют префиксную версию.

Но, как вы указываете для int, фактически нет никакой разницы из-за возможной оптимизации компилятора.

17
25.08.2008 04:29:02
Я думаю, что его вопрос был направлен на C, но для C ++ вы абсолютно правы, и, кроме того, люди C должны принять это, так как они могут использовать его и для C ++. Я слишком часто вижу программистов на C, использующих синтаксис postfix ;-)
Anders Rune Jensen 16.12.2008 15:39:37
-1 Хотя это хороший совет для программистов на C ++, он ни в малейшей степени не отвечает на вопрос, помеченный тегом C. В Си абсолютно не имеет значения, используете ли вы префикс или постфикс.
Lundin 21.10.2014 06:41:22
Префикс @Lundin и postifx имеют значение в C согласно ответу Андреаса . Вы не можете предполагать, что компилятор будет оптимизировать, и это хорошая практика для любого языка, который Alwas предпочитает ++ i над i ++
JProgrammer 24.10.2014 03:02:09
@JProgrammer Они не имеют значения в соответствии с вашим искренним ответом :) Если вы обнаружите, что они дают другой код, то это либо потому, что вы не включили оптимизацию, либо потому, что компилятор плохой. Во всяком случае, ваш ответ не по теме, так как вопрос был о C.
Lundin 24.10.2014 06:17:09

Лучшим ответом будет то, ++iчто иногда будет быстрее, но никогда не медленнее.

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

Однако, если iэто сложный тип, вы можете найти измеримую разницу. Потому что i++вы должны сделать копию своего класса, прежде чем увеличивать его. В зависимости от того, что задействовано в копировании, оно действительно может быть медленнее, поскольку ++itвы можете просто вернуть окончательное значение.

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Другое отличие состоит в том, что у ++iвас есть возможность вернуть ссылку вместо значения. Опять же, в зависимости от того, что входит в создание копии вашего объекта, это может быть медленнее.

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

46
6.10.2015 19:25:20
Вопрос явно говорит о C, без ссылки на C ++.
Dan Cristoloveanu 10.10.2008 23:00:49
Этот (по общему признанию) старый вопрос касался C, а не C ++, но я полагаю, что стоит также упомянуть, что в C ++, даже если класс реализует операторы пост- и предфикса, они даже не обязательно связаны между собой. Например, bar ++ может увеличивать один элемент данных, в то время как ++ bar может увеличивать другой элемент данных, и в этом случае у вас не будет возможности использовать любой из них, так как семантика различна.
Kevin 10.03.2014 19:51:38
-1 Хотя это хороший совет для программистов на C ++, он ни в малейшей степени не отвечает на вопрос, помеченный тегом C. В Си абсолютно не имеет значения, используете ли вы префикс или постфикс.
Lundin 21.10.2014 06:42:22
@Pacerier Вопрос помечен только C и C. Почему вы предполагаете, что они не заинтересованы в этом? Учитывая, как работает SO, не лучше ли предположить, что они заинтересованы только в C, а не в Java, C #, COBOL или любом другом не по теме языке?
Lundin 3.06.2015 06:51:05

Вот дополнительное наблюдение, если вы беспокоитесь о микрооптимизации. Уменьшающие циклы могут «возможно» быть более эффективными, чем увеличивающиеся циклы (в зависимости от архитектуры набора команд, например, ARM), учитывая:

for (i = 0; i < 100; i++)

На каждом цикле у вас будет одна инструкция для каждого:

  1. Добавление 1к i.
  2. Сравните, iменьше ли 100.
  3. Условная ветвь, если iменьше чем 100.

Принимая во внимание, что убывающий цикл:

for (i = 100; i != 0; i--)

Цикл будет иметь инструкцию для каждого из:

  1. Уменьшение i, установка флага состояния регистра ЦП.
  2. Условная ветвь в зависимости от состояния регистра процессора ( Z==0).

Конечно, это работает только при уменьшении до нуля!

Вспомнил из руководства разработчика системы ARM.

16
6.10.2015 18:59:26
Неплохо. Но разве это не создает меньше попаданий в кеш?
AndreasT 11.08.2009 14:17:01
В книгах по оптимизации кода есть старый странный трюк, сочетающий преимущество ветки с нулевым тестом и увеличенного адреса. Вот пример на языке высокого уровня (вероятно, бесполезный, так как многие компиляторы достаточно умны, чтобы заменить его менее эффективным, но более распространенным циклическим кодом): int a [N]; для (i = -N; i; ++ i) a [N + i] + = 123;
noop 23.07.2010 23:38:45
@noop Не могли бы вы уточнить, на какие книги по оптимизации кода вы ссылаетесь? Я изо всех сил пытался найти хорошие.
mezamorphic 14.05.2012 12:45:08
Этот ответ не является ответом на вопрос вообще.
monokrome 5.07.2012 17:54:25
@mezamorphic Для x86 мои источники: руководства по оптимизации Intel, Агнер Фог, Пол Се, Майкл Абраш.
noop 2.10.2012 09:54:24

От эффективности против намерения Эндрю Кениг:

Во-первых, далеко не очевидно, что ++iэто более эффективно, чем i++, по крайней мере, когда речь идет о целочисленных переменных.

И :

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

Итак, если полученное значение не используется, я бы использовал ++i. Но не потому, что он более эффективен: потому что он правильно формулирует мои намерения.

111
6.10.2015 19:20:01
Давайте не будем забывать, что другие унарные операторы также являются префиксами. Я думаю, что ++ i - это «семантический» способ использования унарного оператора, в то время как i ++ используется для удовлетворения конкретных потребностей (оценка перед добавлением).
TM. 12.08.2009 21:55:42
Если полученное значение не используется, нет разницы в семантике: т. Е. Нет оснований для предпочтения той или иной конструкции.
Eamon Nerbonne 17.09.2010 09:20:49
Как читатель, я вижу разницу. Поэтому, как писатель, я покажу свое намерение, выбрав одно из другого. У меня просто есть привычка пытаться общаться с друзьями и товарищами по программированию с помощью кода :)
Sébastien RoccaSerra 17.09.2010 09:47:45
Следуя совету Кенига, я кодирую i++то же самое, что и код i += nили i = i + n, т. Е. В целевом объекте глагола формы , с целевым операндом слева от оператора глагола . В случае , нет правильного объекта , но правило все еще применяется, сохраняя цель слева от оператора глагола . i++
David R Tribble 27.01.2014 18:07:59
Если вы пытаетесь увеличить i, то i ++ и ++ i оба правильно выражают ваше намерение. Нет причин предпочитать одно другому. Как читатель, я не вижу разницы, смущаются ли ваши коллеги, когда видят i ++ и думают, может быть, это опечатка, и он не хотел увеличивать i?
dan carter 12.02.2014 01:49:26

Пожалуйста, не позволяйте, чтобы вопрос о том, «какой из них быстрее», стал решающим фактором для использования. Скорее всего, вы никогда не будете беспокоиться об этом, и, кроме того, время чтения программистом намного дороже, чем машинное время.

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

11
20.09.2008 05:09:18
Я считаю, что неправильно предпочесть нечеткие улучшения читаемости фактическому повышению эффективности и общей ясности намерений.
noop 21.07.2010 11:53:14
Мой термин «время чтения программистом» примерно аналогичен «ясности намерений». «Фактический прирост эффективности» часто неизмерим, достаточно близок к нулю, чтобы назвать их нулем. В случае OP, если код не был профилирован, чтобы обнаружить, что ++ i является узким местом, вопрос о том, какой из них быстрее, является пустой тратой времени и мыслительными единицами программиста.
Andy Lester 22.07.2010 23:23:56
Разница в удобочитаемости между ++ i и i ++ является лишь вопросом личных предпочтений, но ++ i явно подразумевает более простую операцию, чем i ++, несмотря на то, что результат является эквивалентным для тривиальных случаев и простых типов данных при оптимизации компилятора. Поэтому ++ я для меня победитель, когда конкретные свойства post-increment не нужны.
noop 23.07.2010 23:17:18
Вы говорите то, что я говорю. Гораздо важнее показать намерение и улучшить читабельность, чем беспокоиться об «эффективности».
Andy Lester 26.07.2010 15:26:06
Я до сих пор не могу согласиться. Если читаемость стоит выше в вашем списке приоритетов, то, возможно, вы выбрали неправильный язык программирования. Основная цель C / C ++ - написание эффективного кода.
noop 8.08.2013 21:38:55

Я всегда предпочитаю предварительное увеличение, однако ...

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

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

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

2
16.10.2008 11:28:13
Ваша публикация специфична для C ++, в то время как вопрос касается C. В любом случае, ваш ответ неверен: для пользовательских операторов постинкрементного компилятора, как правило, не удастся создать столь эффективный код.
Konrad Rudolph 16.10.2008 11:31:13
Он заявляет «если функция становится встроенной», и это делает его рассуждение правильным.
Blaisorblade 15.01.2009 00:21:49
Поскольку C не обеспечивает перегрузку операторов, вопрос до и после сообщения в основном неинтересен. Компилятор может оптимизировать неиспользуемый темп, используя ту же логику, которая применяется к любому другому неиспользуемому, примитивно вычисляемому значению. Смотрите выбранный ответ для образца.
Andrew Eidsness 16.01.2009 13:09:55

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

Я только что протестировал его с компиляторами, которые мы используем в нашем текущем проекте, и 3 из 4 не оптимизируют его.

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

Если у вас нет действительно глупой реализации одного из операторов в вашем коде:

Я всегда предпочитаю ++, а не i ++.

7
9.02.2009 15:40:45
Просто любопытно ... почему вы используете 4 разных компилятора Си в одном проекте? Или в одной команде или в одной компании?
Lawrence Dol 18.01.2013 19:19:30
При создании игр для консолей каждая платформа использует собственный компилятор / набор инструментов. В идеальном мире мы могли бы использовать gcc / clang / llvm для всех целей, но в этом мире нам приходится мириться с Microsoft, Intel, Metroworks, Sony и т. Д.
Andreas 26.01.2013 13:24:52

Я могу вспомнить ситуацию, когда постфикс медленнее, чем приращение префикса:

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

Теперь представьте следующую программу и ее перевод в гипотетическую сборку:

Приращение префикса:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Постфиксный прирост:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

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

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


В качестве дополнительного примечания я хотел бы упомянуть, что выражение, в котором есть выражение, b++нельзя просто преобразовать в выражение ++bбез каких-либо дополнительных усилий (например, добавив a - 1). Таким образом, сравнение двух, если они являются частью какого-либо выражения, не совсем корректно. Часто, когда вы используете b++внутри выражения, которое вы не можете использовать ++b, так что даже если бы он ++bбыл потенциально более эффективным, это было бы просто неправильно. Исключение, конечно, если выражение a = b++ + 1;требует его (например, который может быть изменен на a = ++b;).

4
4.06.2014 17:57:51

Прежде всего: разница между i++и ++iпренебрежимо мала в C.


К деталям.

1. Хорошо известная проблема C ++: ++iбыстрее

В C ++ ++iболее эффективен, если iэто какой-то объект с перегруженным оператором приращения.

Почему?
В ++i, объект сначала увеличивается, и впоследствии может быть передан как постоянная ссылка на любую другую функцию. Это невозможно, если выражение связано с foo(i++)тем, что теперь необходимо выполнить приращение до вызова foo(), но старое значение необходимо передать foo(). Следовательно, компилятор вынужден делать копию iдо того, как он выполнит оператор приращения в оригинале. Дополнительные вызовы конструктора / деструктора являются плохой частью.

Как отмечено выше, это не относится к фундаментальным типам.

2. Малоизвестный факт: i++ может быть быстрее

Если нет необходимости вызывать конструктор / деструктор, что всегда имеет место в C ++iи i++должно быть одинаково быстрым, верно? Нет. Они практически одинаково быстрые, но могут быть небольшие различия, которые большинство других опрошенных неправильно поняли.

Как может i++быть быстрее?
Дело в зависимости от данных. Если значение необходимо загрузить из памяти, необходимо выполнить две последующие операции с ним, увеличивая его и используя его. С ++i, увеличение должно быть сделано, прежде чем значение может быть использовано. При i++этом использование не зависит от приращения, и ЦП может выполнять операцию использования параллельно с операцией приращения. Разница составляет не более одного цикла процессора, поэтому он действительно пренебрежимо мал, но он есть. И наоборот, тогда многие ожидали.

11
4.06.2014 18:34:44
О вашей точке 2: Если ++iили i++используется в другом выражении, изменение между ними изменяет семантику выражения, поэтому любой возможный выигрыш / потеря производительности не подлежит сомнению. Если они автономны, т. Е. Результат операции не используется сразу, то любой приличный компилятор скомпилирует его с тем же самым, например, с INCинструкцией по сборке.
Shahbaz 2.08.2015 15:20:47
@ Shahbaz Это совершенно верно, но не в этом суть. 1) Несмотря на то, что семантика различны, как i++и ++iмогут быть взаимозаменяемыми в почти каждой возможной ситуации путем корректировки констант петли по одной, поэтому они эквивалент в то , что они делают для программиста. 2) Несмотря на то, что оба компилируют одну и ту же инструкцию, их выполнение для ЦП различно. В случае i++ЦПУ может вычислить приращение параллельно с какой-либо другой инструкцией, которая использует то же значение (ЦП действительно делают это!), В то время ++iкак ЦП должен запланировать другую инструкцию после приращения.
cmaster - reinstate monica 2.08.2015 19:56:11
@Shahbaz Как пример: if(++foo == 7) bar();и if(foo++ == 6) bar();функционально эквивалентны. Тем не менее, второй может быть на один цикл быстрее, потому что ЦП может вычислять сравнение и приращение параллельно. Не то чтобы этот единственный цикл имел большое значение, но разница есть.
cmaster - reinstate monica 2.08.2015 20:03:21
Хорошая точка зрения. Константы часто отображаются (а также, <например, против <=), где ++обычно используется, поэтому преобразование между ними часто легко возможно.
Shahbaz 3.08.2015 16:14:54
Мне нравится пункт 2, но это применимо, только если используется значение, верно? Вопрос говорит: «Если полученное значение не используется?», То это может сбить с толку.
jinawee 1.01.2019 19:55:28

Короткий ответ:

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

Длинный ответ:

В каждом другом ответе не упоминается о том, что различие между « ++iпротив» i++имеет смысл только в том выражении, в котором оно найдено.

В случае с for(i=0; i<n; i++), i++выражение само по себе одно: перед точкой есть точка последовательности, i++а после нее - точка . Таким образом, единственный генерируемый машинный код - это «увеличение iна 1», и четко определено, как это упорядочено по отношению к остальной части программы. Так что, если вы измените его на префикс ++, это не будет иметь никакого значения, вы все равно просто получите машинный код « iна 1».

Различия между ++iи i++только имеют значение в выражениях, таких как array[i++] = x;против array[++i] = x;. Некоторые могут поспорить и сказать, что постфикс будет медленнее в таких операциях, потому что регистр, в котором он iнаходится, должен быть перезагружен позже. Но затем обратите внимание, что компилятор может свободно упорядочивать ваши инструкции любым удобным для них способом, если только он не «нарушает поведение абстрактной машины», как это называется в стандарте C.

Так что пока вы можете предполагать, что array[i++] = x;переводится в машинный код как:

  • Сохранить значение iв регистре А.
  • Сохранить адрес массива в регистре B.
  • Добавьте A и B, сохраните результаты в A.
  • По этому новому адресу, представленному буквой A, сохраните значение x.
  • Сохранять значение iв регистре A // неэффективно, потому что здесь дополнительная инструкция, мы уже делали это один раз.
  • Инкрементный регистр А.
  • Хранить регистр А в i.

компилятор может также сгенерировать код более эффективно, например:

  • Сохранить значение iв регистре А.
  • Сохранить адрес массива в регистре B.
  • Добавьте A и B, сохраните результаты в B.
  • Инкрементный регистр А.
  • Хранить регистр А в i.
  • ... // остальная часть кода.

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

Так что нет никакой разницы между префиксом и постфиксом ++в C. Теперь то, чем вы, как программистом на C, должны быть разными, это люди, которые в некоторых случаях непостоянно используют префикс, а в других - postfix, без объяснения причин. Это говорит о том, что они не уверены в том, как работает C, или что они неправильно владеют языком. Это всегда плохой признак, он в свою очередь предполагает, что они принимают другие сомнительные решения в своей программе, основанные на суеверии или «религиозных догмах».

«Префикс ++всегда быстрее» - действительно одна из таких ложных догм, которая распространена среди потенциальных программистов на Си.

17
26.02.2016 07:23:50
Никто не сказал: «Префикс ++ всегда быстрее». Это неправильно процитировано. Они сказали: «Postfix ++ никогда не бывает быстрее».
Pacerier 17.02.2015 06:25:09
@Pacerier Я не цитирую какого-то конкретного человека, я просто распространяю неправильные убеждения.
Lundin 3.06.2015 06:37:29
@rbaleksandar C ++! = C.
Lundin 31.08.2015 06:17:09
@rbaleksandar Вопрос и ответ, оба говорят о C ++. Который отличается от C, так как имеет перегрузку операторов и конструкторы копирования и т. Д.
Lundin 31.08.2015 10:36:20
@rbaleksandar c ++ == c && ++ c! = c
sadljkfhalskdjfh 7.10.2015 16:42:22

Я прочитал большинство ответов здесь и многие комментарии, и я не увидел ни одного упоминания об одном случае, где я мог бы подумать, где i++более эффективно, чем ++i(и, возможно, на удивление, --i было более эффективно, чем i--). Это для компиляторов C для DEC PDP-11!

У PDP-11 были инструкции по сборке для предварительного уменьшения регистра и постинкремента, но не наоборот. Инструкции позволили использовать любой регистр «общего назначения» в качестве указателя стека. Так что если вы используете что-то подобное, *(i++)то можете скомпилировать в одну инструкцию по сборке, а *(++i)не сможете .

Это, очевидно, очень эзотерический пример, но он предоставляет исключение, когда постинкремент более эффективен (или, я должен сказать, был , так как в наши дни нет большого спроса на код PDP-11 C).

4
15.08.2019 22:45:11
Очень эзотерично, но очень интересно!
Mark Harrison 18.08.2019 21:08:24
@daShier. +1, хотя я не согласен, это не так эзотерично, по крайней мере, так не должно быть. C был разработан совместно с Unix в AT & T Bell Labs в начале 70-х годов, когда PDP-11 был целевым процессором. В исходном коде Unix из этой эпохи постинкремент «i ++» более распространен отчасти потому, что разработчики знали, когда присваивается значение «j = i ++» или используется в качестве индекса «a [i ++] = n», код будет немного быстрее (и меньше). Похоже, они привыкли использовать постинкремент, если предварительно не требуется. Другие учились, читая их код, а также подхватили эту привычку.
jimhark 4.10.2019 00:25:08
68000 имеет ту же функцию, пост-инкремент и пре-декремент поддерживаются аппаратно (как режимы адресации), но не наоборот. Команда Motorola была вдохновлена ​​DEC PDP-11.
jimhark 4.10.2019 00:31:20
@jimhark, да, я один из тех программистов PDP-11, которые перешли на 68000 и до сих пор используют --iи i++.
daShier 4.10.2019 04:02:09