C: Яка різниця між ++ i та i ++?


887

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


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

Відповіді:


1099
  • ++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++оскільки він має форму "операнд-оператор", а-ля - присвоєння "значення операнда-оператора-значення". Іншими словами, цільовий операнд знаходиться з лівого боку виразу, як і в операторі призначення.
David R Tribble

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, тому що в типовому для циклу немає побічного ефекту (наприклад, призначення) у частині ++ i.
Марк Гаррісон

175

i ++ відомий як " Пост інкремент", тоді як ++ i називається " попереднім збільшенням".

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(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) відступ, якщо ви насправді не потребуєте старого значення до (inc | dec), яке дуже мало хто робить, і все ж яке дивовижне число використання передбачуваних навчальних матеріалів, створити вантажний культ користувачів постфіксів, які навіть не знають, що це "..!
підкреслити_29

Я не впевнений, що "компілятори в наші дні ... подбайте про ці речі" - це правда. У спеціальній operator++(int)(postfix версії) коді в значній мірі необхідно створити тимчасовий, який буде повернутий. Ви впевнені, що компілятори завжди можуть оптимізувати це?
Пітер -

35

++i збільшує значення, а потім повертає його.

i++ повертає значення, а потім збільшує його.

Це тонка різниця.

Для циклу for, використовуйте ++i, оскільки це трохи швидше. i++створить додаткову копію, яка просто викидається.


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

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

31

i++: У цьому сценарії спочатку присвоюється значення, а потім відбувається приріст.

++i: У цьому сценарії спочатку робиться приріст, а потім присвоюється значення

Нижче представлена ​​візуалізація зображення, а також ось приємне практичне відео, яке демонструє те саме.

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


Як можна збільшити певний приріст?
kouty

@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 of for циклу: i++використовується в основному тому, що, як правило, ми використовуємо вихідне значення iдо збільшення для циклу. Але залежно від логіки вашої програми вона може змінюватися.


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 ++. Це принцип, який ви повинні використовувати, вирішуючи, який використовувати:

Скажіть, що ви маєте на увазі під кодом.

Якщо у вашому виписці вам не потрібна цінність перед збільшенням, не використовуйте цю форму оператора. Це незначна проблема, але якщо ви не працюєте з посібником зі стилів, який взагалі забороняє одну версію на користь іншої (він називається керівництвом по стилю з кісткою), ви повинні використовувати форму, яка найбільш точно виражає те, що ви намагаєтеся зробити.

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 ++ ( після збільшення ) та
  • ++ i Pre ( до збільшення )

    • i =1 додати, якщо збільшується цикл, як1,2,3,4,n
    • попередньо, якщо i =1 наріст циклу подобається2,3,4,5,n

5

i ++ і ++ i

Цей невеликий код може допомогти візуалізувати різницю з іншого кута, ніж уже опубліковані відповіді:

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

Наступний фрагмент коду С ілюструє різницю між операторами до збільшення та після збільшення та зменшення:

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

Якщо мова йде про операторів АБО І І, це стає цікавішим.

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 ++ пост / попереднє збільшення змінної вказівника

#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(int i=0; i<10; i++)

має той же ефект, що і

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

Правило пам’ятати

Щоб не робити плутанини між двома операторами, я прийняв це правило:

Пов’яжіть позицію оператора ++щодо змінної iдо порядку ++операції щодо призначення

Іншими словами:

  • ++ перед тим, як 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, а також значення i12)


11
Це спричиняє невизначену поведінку, оскільки між i++та++i
ММ

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