C: В чем разница между ++ i и i ++?


889

В C, в чем разница между использованием ++iи i++, и что следует использовать в блоке приращения forцикла?


10
Не уверен, что оригинальный постер заинтересован, но в C ++ разница в производительности может быть существенной, поскольку создание временного объекта может быть дорогостоящим для определенного пользователем типа.
На Фрейнде

Ответы:


1102
  • ++iбудет увеличивать значение i, а затем возвращать увеличенное значение.

     i = 1;
     j = ++i;
     (i is 2, j is 2)
    
  • i++будет увеличивать значение i, но возвращать исходное значение, которое iудерживалось до увеличения.

     i = 1;
     j = i++;
     (i is 2, j is 1)
    

Для forцикла либо работает. ++iкажется более распространенным, может быть , потому что это то , что используется в K & R .

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

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

Вопрос эффективности интересен ... вот моя попытка ответить: есть ли разница в производительности между i ++ и ++ i в C?

Как отмечает @OnFreund , он отличается для объекта C ++, поскольку operator++()это функция, и компилятор не может знать, как оптимизировать создание временного объекта для хранения промежуточного значения.


6
Разве этот эффект не выдержит повторение цикла после достижения конечного состояния? Например, for(int i=0; i<10; i++){ print i; } не будет ли это отличаться от for(int i=0; i<10; ++i){ print i; } моего понимания того, что некоторые языки будут давать разные результаты в зависимости от того, что вы используете.
aVeRTRAC

27
jonnyflash, оба будут работать одинаково, поскольку приращение i и print находятся в разных выражениях. Это должно иметь место для любого языка, который поддерживает C-стиль ++. Единственное различие между ++ i и i ++ будет при использовании значения операции в одном выражении.
Марк Харрисон

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

2
@MarkHarrison, он будет работать одинаково не потому, что i++и print iесть в разных утверждениях, а потому, что i++;и i<10есть. Замечание @ jonnyflash не совсем так. Предположим, у вас есть for(int i=0; i++<10){ print i; }и for(int i=0; ++i<10){ print i; }. Они будут работать по-другому, как описано @johnnyflash в первом комментарии.
Адам

3
@sam, потому что в типичном цикле for нет побочного эффекта (например, присваивания) в части ++ i.
Марк Харрисон

175

i ++ известен как Post Increment, тогда как ++ i называется Pre Increment.

i++

i++это постинкремент, потому что он увеличивает iзначение на 1 после завершения операции.

Давайте посмотрим на следующий пример:

int i = 1, j;
j = i++;

Здесь ценность j = 1но i = 2. Здесь значение iбудет присвоено jпервому, а затем iбудет увеличено.

++i

++iявляется предварительным приращением, потому что оно увеличивает iзначение на 1 перед операцией. Значит j = i;выполнится после i++.

Давайте посмотрим на следующий пример:

int i = 1, j;
j = ++i;

Здесь ценность j = 2но i = 2. Здесь значение iбудет присвоено jпосле i увеличения i. Аналогично ++iбудет выполнен раньше j=i;.

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

for(i=0; i<5; i++)
   printf("%d ",i);

А также

for(i=0; i<5; ++i)
   printf("%d ",i);

Оба цикла будут давать одинаковый результат. то есть 0 1 2 3 4.

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

for(i = 0; i<5;)
    printf("%d ",++i);

В этом случае вывод будет 1 2 3 4 5.


1
Инициализация переменных после префикса и после исправления помогает понять. Спасибо.
Абдул Алим Шакир

42

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


1
что, я надеюсь, означает « использовать префикс (inc | dec) rement, если только вам на самом деле не нужно старое значение до (inc | dec)», как это делают очень немногие, и, тем не менее, непонятная доля предполагаемых учебных материалов, создание культового груза пользователей постфикса, которые даже не знают, что это такое
underscore_d

Я не уверен, что "компиляторы в наши дни ... заботятся об этих вещах" универсально верно. В обычае operator++(int)(постфиксная версия) код должен создать временный объект, который будет возвращен. Вы уверены, что компиляторы всегда могут это оптимизировать?
Питер - Восстановить Монику

36

++i увеличивает значение, затем возвращает его.

i++ возвращает значение, а затем увеличивает его.

Это тонкая разница.

Для цикла for используйте ++i, так как он немного быстрее. i++создаст дополнительную копию, которую просто выбрасывают.


23
Я не знаю ни о каком компиляторе, где он имеет значение по крайней мере для целых чисел.
blabla999

4
Это не быстрее . Значения игнорируются (эффективен только побочный эффект), и компилятор может / будет генерировать точно такой же код.
wildplasser

31

i++: В этом сценарии сначала присваивается значение, а затем происходит приращение.

++i: В этом сценарии сначала выполняется приращение, а затем присваивается значение

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

введите описание изображения здесь


Как вы можете увеличить немного не назначен?
Куты

@kouty Вы можете увеличить регистр, не назначенный переменной.
Поллукс

20

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

Я стараюсь не слишком полагаться на оптимизацию компиляторов, поэтому следую совету Райана Фокса: когда я могу использовать оба, я использую ++i.


11
-1 для C ++ ответ на C вопрос. Нет более локальной копии значения, iчем значение 1, когда вы пишете оператор 1;.
R .. GitHub ОСТАНОВИТЬ ЛЬДА

14

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

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

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

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

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

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

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


13

Они оба увеличивают число. ++iэквивалентно i = i + 1.

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

Пример:

int i = 1;
int x = i++; //x is 1, i is 2
int y = ++i; //y is 3, i is 3

8

++i(Операция Префикса): приращения , а затем присваивает значение
(например): int i = 5, int b = ++i в этом случае, 6 назначаются б, а затем приращения до 7 и так далее.

i++(Операция Постфикса): Назначает , а затем увеличивает значение
(например): int i = 5, int b = i++ в этом случае 5 назначаются б, а затем приращения до 6 и так далее.

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


7

++i: преинкрементный, другой постинкрементный.

i++: получает элемент, а затем увеличивает его.
++i: увеличивает i, а затем возвращает элемент.

Пример:

int i = 0;
printf("i: %d\n", i);
printf("i++: %d\n", i++);
printf("++i: %d\n", ++i);

Вывод:

i: 0
i++: 0
++i: 2

5

Я полагаю, вы понимаете разницу в семантике (хотя, честно говоря, мне интересно, почему люди задают вопросы «что означает оператор X» о переполнении стека, а не читают, вы знаете, книгу или веб-учебник или что-то в этом роде).

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

Скажите, что вы имеете в виду в коде.

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

QED, используйте версию перед инкрементом:

for (int i = 0; i != X; ++i) ...

5

Разницу можно понять по следующему простому коду C ++:

int i, j, k, l;
i = 1; //initialize int i with 1
j = i+1; //add 1 with i and set that as the value of j. i is still 1
k = i++; //k gets the current value of i, after that i is incremented. So here i is 2, but k is 1
l = ++i; // i is incremented first and then returned. So the value of i is 3 and so does l.
cout << i << ' ' << j << ' ' << k << ' '<< l << endl;
return 0;

5

Главное отличие

  • i ++ Post ( после увеличения ) и
  • ++ я Pre ( до приращения )

    • опубликовать, если i =1 цикл увеличивается как1,2,3,4,n
    • предварительно, если i =1 цикл увеличивается как2,3,4,5,n

5

я ++ и ++ я

Этот небольшой код может помочь визуализировать разницу под другим углом, чем уже опубликованные ответы:

int i = 10, j = 10;

printf ("i is %i \n", i);
printf ("i++ is %i \n", i++);
printf ("i is %i \n\n", i);

printf ("j is %i \n", j);
printf ("++j is %i \n", ++j);
printf ("j is %i \n", j);

Результат:

//Remember that the values are i = 10, and j = 10

i is 10 
i++ is 10     //Assigns (print out), then increments
i is 11 

j is 10 
++j is 11    //Increments, then assigns (print out)
j is 11 

Обратите внимание на ситуации до и после.

для цикла

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

int i, j;

for (i = 0; i <= 3; i++)
    printf (" > iteration #%i", i);

printf ("\n");

for (j = 0; j <= 3; ++j)
    printf (" > iteration #%i", j);

Результат:

> iteration #0 > iteration #1 > iteration #2 > iteration #3
> iteration #0 > iteration #1 > iteration #2 > iteration #3 

Я не знаю о вас, но я не вижу никакой разницы в его использовании, по крайней мере, в цикле for.


5

Следующий фрагмент кода C иллюстрирует различие между операторами увеличения и уменьшения до и после операции:

int  i;
int  j;

Операторы приращения:

i = 1;
j = ++i;    // i is now 2, j is also 2
j = i++;    // i is now 3, j is 2

4

Предварительная обработка означает приращение на той же строке. Постинкремент означает приращение после выполнения строки.

int j=0;
System.out.println(j); //0
System.out.println(j++); //0. post-increment. It means after this line executes j increments.

int k=0;
System.out.println(k); //0
System.out.println(++k); //1. pre increment. It means it increments first and then the line executes

Когда речь идет об операторах OR, AND, становится интереснее.

int m=0;
if((m == 0 || m++ == 0) && (m++ == 1)) { //false
/* in OR condition if first line is already true then compiler doesn't check the rest. It is technique of compiler optimization */
System.out.println("post-increment "+m);
}

int n=0;
if((n == 0 || n++ == 0) && (++n == 1)) { //true
System.out.println("pre-increment "+n); //1
}

В массиве

System.out.println("In Array");
int[] a = { 55, 11, 15, 20, 25 } ;
int ii, jj, kk = 1, mm;
ii = ++a[1]; // ii = 12. a[1] = a[1] + 1
System.out.println(a[1]); //12

jj = a[1]++; //12
System.out.println(a[1]); //a[1] = 13

mm = a[1];//13
System.out.printf ( "\n%d %d %d\n", ii, jj, mm ) ; //12, 12, 13

for (int val: a) {
     System.out.print(" " +val); //55, 13, 15, 20, 25
}

В C ++ post / pre-инкремент переменной указателя

#include <iostream>
using namespace std;

int main() {

    int x=10;
    int* p = &x;

    std::cout<<"address = "<<p<<"\n"; //prints address of x
    std::cout<<"address = "<<p<<"\n"; //prints (address of x) + sizeof(int)
    std::cout<<"address = "<<&x<<"\n"; //prints address of x

    std::cout<<"address = "<<++&x<<"\n"; //error. reference can't re-assign because it is fixed (immutable)
}

4

Коротко:

++iи i++работает так же, если вы не пишете их в функции. Если вы используете что-то вроде function(i++)или function(++i)вы можете увидеть разницу.

function(++i)говорит первое увеличение i на 1, после этого поместите это iв функцию с новым значением.

function(i++)говорит, поместите сначала iв функцию после этого увеличения iна 1.

int i=4;
printf("%d\n",pow(++i,2));//it prints 25 and i is 5 now
i=4;
printf("%d",pow(i++,2));//it prints 16 i is 5 now

2
Разница на самом деле не связана с вызовами функций (и вы можете заметить разницу, не делая вызовов функций). Есть разница между int j = ++i;и int k = i++;даже тогда, когда нет вызова функции.
Джонатан Леффлер

3

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

Этот код и его вывод объясняют разницу:

#include<stdio.h>

int main(int argc, char* argv[])
{
  unsigned int i=0, a;
  a = i++;
  printf("i before: %d; value returned by i++: %d, i after: %d\n", i, a, i);
  i=0;
  a = ++i;
  printf("i before: %d; value returned by ++i: %d, i after: %d\n", i, a, i);
}

Выход:

i before: 1; value returned by i++: 0, i after: 1
i before: 1; value returned by ++i: 1, i after: 1

Таким образом, в основном ++iвозвращает значение после его увеличения, в то время как ++iвозвращает значение до его увеличения. В конце в обоих случаях значение iбудет увеличено.

Другой пример:

#include<stdio.h>

int main ()
  int i=0;
  int a = i++*2;
  printf("i=0, i++*2=%d\n", a);
  i=0;
  a = ++i * 2;
  printf("i=0, ++i*2=%d\n", a);
  i=0;
  a = (++i) * 2;
  printf("i=0, (++i)*2=%d\n", a);
  i=0;
  a = (++i) * 2;
  printf("i=0, (++i)*2=%d\n", a);
  return 0;
}

Вывод:

i=0, i++*2=0
i=0, ++i*2=2
i=0, (++i)*2=2
i=0, (++i)*2=2

Много раз нет разницы

Различия очевидны , когда возвращаемое значение присваиваются другим переменным или , когда приращение выполняются в конкатенации с другими операциями , где применяются операция приоритет ( i++*2отличается от ++i*2, но (i++)*2и (++i)*2возвращает то же значение) во многих случаях они являются взаимозаменяемыми. Классическим примером является синтаксис цикла for:

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

имеет тот же эффект

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

Правило помнить

Чтобы не путать два оператора, я принял это правило:

Свяжите позицию оператора ++относительно переменной iс порядком ++операции относительно назначения

Сказал другими словами:

  • ++ before i Приращение средств должно быть выполнено перед присваиванием;
  • ++ после i приращения средства должно быть выполнено после назначения:

3

Вы можете думать о внутреннем преобразовании этого как о множественных утверждениях ;

  • Дело 1
i++;

Вы можете думать это как,

i;
i = i+1;
  • случай 2
++i;

Вы можете думать это как,

i = i+i;
i;

-3

a = i ++ означает, что содержит текущее значение i a = ++ i означает, что содержит увеличенное значение i


10
Этот ответ не точен. a = i++;означает, что значение, сохраненное в, aбудет значением iдо приращения, но «без приращения» подразумевает, что iэто не приращение, что совершенно неправильно - iувеличивается, но значение выражения является значением до приращения.
Джонатан Леффлер

-6

Вот пример, чтобы понять разницу

int i=10;
printf("%d %d",i++,++i);

вывод: 10 12/11 11(в зависимости от порядка вычисления аргументов printfфункции, который варьируется в зависимости от компиляторов и архитектур)

Пояснение: i++-> iпечатается, а затем увеличивается. (Печатает 10, но iстановится 11) ++i-> iзначение увеличивается и печатает значение. (Печатает 12, а значение iтакже 12)


11
Это приводит к неопределенному поведению , так как не имеет смысла последовательности между i++и++i
ММ

@Lundin это правильно, хотя, LHS, RHS запятой имеют точку последовательности между ними, но эти два выражения по-прежнему не последовательны друг с другом
Антти Хаапала
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.