Чи є різниця в ++i
і i++
в for
циклі? Це просто річ синтаксису?
Чи є різниця в ++i
і i++
в for
циклі? Це просто річ синтаксису?
Відповіді:
a ++ відомий як постфікс.
додати 1 до a, повертає старе значення.
++ a відомий як префікс.
додати 1 до а, повертає нове значення.
C #:
string[] items = {"a","b","c","d"};
int i = 0;
foreach (string item in items)
{
Console.WriteLine(++i);
}
Console.WriteLine("");
i = 0;
foreach (string item in items)
{
Console.WriteLine(i++);
}
Вихід:
1
2
3
4
0
1
2
3
foreach
і while
петлі залежать від того, який тип приросту ви використовуєте. Для циклів, як показано нижче, це не має значення, оскільки ви не використовуєте значення повернення i:
for (int i = 0; i < 5; i++) { Console.Write(i);}
Console.WriteLine("");
for (int i = 0; i < 5; ++i) { Console.Write(i); }
0 1 2 3 4
0 1 2 3 4
Якщо використовується оцінене значення, то тип приросту стає значущим:
int n = 0;
for (int i = 0; n < 5; n = i++) { }
Попереднє збільшення ++ i збільшує значення i і оцінює до нового збільшення.
int i = 3;
int preIncrementResult = ++i;
Assert( preIncrementResult == 4 );
Assert( i == 4 );
Посилення i ++ збільшує значення i та оцінює до початкового непосиленого значення.
int i = 3;
int postIncrementResult = i++;
Assert( postIncrementtResult == 3 );
Assert( i == 4 );
У C ++ зазвичай переважний попередній приріст, де ви можете використовувати будь-який.
Це тому, що якщо ви використовуєте пост-інкремент, він може вимагати від компілятора генерувати код, який створює додаткову тимчасову змінну. Це тому, що і попереднє, і нове значення зміненої змінної потрібно десь утримувати, оскільки вони можуть знадобитися в іншому місці вираження, що оцінюється.
Так, принаймні, у C ++ може бути різниця в продуктивності, яка керує вашим вибором, який використовувати.
Це головним чином лише проблема, коли змінна, що збільшується, є типом, визначеним користувачем, з перекритим оператором ++. Для примітивних типів (int тощо) різниці в продуктивності немає. Але, варто дотримуватися оператора попереднього збільшення як орієнтир, якщо оператор після нарощування точно не вимагає.
Тут ще кілька дискусій:
https://web.archive.org/web/20170405054235/http://en.allexperts.com/q/C-1040/Increment-operators.htm
У C ++, якщо ви використовуєте STL, ви можете використовувати для циклів з ітераторами. В основному вони мають перевершені оператори ++, тому дотримуватися попереднього збільшення - хороша ідея. Хоча компілятори стають розумнішими весь час, і новіші, можливо, зможуть проводити оптимізацію, що означає відсутність різниці в продуктивності - особливо якщо тип, який збільшується, визначений вбудованим файлом заголовка (як часто це реалізація STL), щоб компілятор міг бачити, як метод реалізований і потім може знати, які оптимізації безпечно виконати. Незважаючи на це, напевно, все-таки варто дотримуватися попереднього збільшення, оскільки цикли виконуються багато разів, а це означає, що невелике покарання за продуктивність може незабаром посилитися.
В інших мовах, таких як C #, де оператор ++ не може бути перевантажений, різниці в продуктивності немає. Використовуються в циклі для просування змінної циклу, оператори до і після збільшення прирівнюються.
Виправлення: перевантаження ++ у C # дозволено. Здається, що в порівнянні з C ++, в c # ви не можете перевантажувати попередню та після версії самостійно. Отже, я вважаю, що якщо результат виклику ++ у C # не присвоєно змінній або не використовується як частина складного виразу, тоді компілятор зменшить до і після версій ++ до коду, що виконує еквівалентну кількість.
У C # немає різниці при використанні в циклі for .
for (int i = 0; i < 10; i++) { Console.WriteLine(i); }
виводить те саме, що і
for (int i = 0; i < 10; ++i) { Console.WriteLine(i); }
Як зазначали інші, при використанні в цілому i ++ і ++ у мене є тонка, але значна істотна різниця:
int i = 0;
Console.WriteLine(i++); // Prints 0
int j = 0;
Console.WriteLine(++j); // Prints 1
i ++ зчитує значення i потім збільшує його.
++ Я збільшує значення i потім зчитує його.
++i
і i++
інше виконують ті самі операції в одному порядку: створити темп-копію i
; збільшити значення temp для отримання нового значення (не перевизначати темп); зберігати нове значення в i
; тепер, якщо це ++i
результат, що повертається, це нове значення; якщо i++
результат повертається тимчасовою копією. Більш докладну відповідь тут: stackoverflow.com/a/3346729/3330348
Питання:
Чи є різниця у ++ i та i ++ у циклі for?
Відповідь: Ні .
Чому кожна відповідь повинна детально пояснювати приріст до та після збільшення, коли про це навіть не задається питанням?
Це для циклу:
for (int i = 0; // Initialization
i < 5; // Condition
i++) // Increment
{
Output(i);
}
Перекладається на цей код без використання циклів:
int i = 0; // Initialization
loopStart:
if (i < 5) // Condition
{
Output(i);
i++ or ++i; // Increment
goto loopStart;
}
Тепер має значення, якщо ви поставите тут i++
або ++i
як приріст? Ні, це не так, оскільки повернене значення збільшення операції є незначним. i
буде збільшуватися ПІСЛЯ виконання коду, який знаходиться всередині корпусу циклу.
Оскільки ви запитуєте про різницю в циклі, я думаю, ви маєте на увазі
for(int i=0; i<10; i++)
...;
У цьому випадку у вас немає різниці в більшості мов: цикл поводиться однаково, незалежно від того, ви пишете i++
і ++i
. У C ++ ви можете написати власні версії операторів ++, і ви можете визначити для них окремі значення, якщо i
це тип визначений користувачем (наприклад, ваш власний клас).
Причина, чому це не має значення вище, полягає в тому, що ви не використовуєте значення i++
. Інша справа, коли ти робиш
for(int i=0, a = 0; i<10; a = i++)
...;
Тепер є різниця, тому що, як зазначають інші, i++
означає приріст, але оцінювати до попереднього значення , але ++i
означає приріст, але оцінювати доi
(таким чином, він би оцінював до нового значення). У наведеному вище випадку a
присвоюється попереднє значення i, тоді як i збільшується.
Як показує цей код (див. Розібраний MSIL у коментарях), компілятор C # 3 не робить різниці між i ++ та ++ i у циклі for. Якщо значення i ++ або ++ я приймали, напевно буде різниця (це було складено у Visutal Studio 2008 / Release Build):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PreOrPostIncrement
{
class Program
{
static int SomethingToIncrement;
static void Main(string[] args)
{
PreIncrement(1000);
PostIncrement(1000);
Console.WriteLine("SomethingToIncrement={0}", SomethingToIncrement);
}
static void PreIncrement(int count)
{
/*
.method private hidebysig static void PreIncrement(int32 count) cil managed
{
// Code size 25 (0x19)
.maxstack 2
.locals init ([0] int32 i)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0014
IL_0004: ldsfld int32 PreOrPostIncrement.Program::SomethingToIncrement
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stsfld int32 PreOrPostIncrement.Program::SomethingToIncrement
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: stloc.0
IL_0014: ldloc.0
IL_0015: ldarg.0
IL_0016: blt.s IL_0004
IL_0018: ret
} // end of method Program::PreIncrement
*/
for (int i = 0; i < count; ++i)
{
++SomethingToIncrement;
}
}
static void PostIncrement(int count)
{
/*
.method private hidebysig static void PostIncrement(int32 count) cil managed
{
// Code size 25 (0x19)
.maxstack 2
.locals init ([0] int32 i)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0014
IL_0004: ldsfld int32 PreOrPostIncrement.Program::SomethingToIncrement
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stsfld int32 PreOrPostIncrement.Program::SomethingToIncrement
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: stloc.0
IL_0014: ldloc.0
IL_0015: ldarg.0
IL_0016: blt.s IL_0004
IL_0018: ret
} // end of method Program::PostIncrement
*/
for (int i = 0; i < count; i++)
{
SomethingToIncrement++;
}
}
}
}
Один (++ i) є попереднім підвищенням, один (i ++) - підвищенням. Різниця полягає в тому, яке значення негайно повертається з виразу.
// Psuedocode
int i = 0;
print i++; // Prints 0
print i; // Prints 1
int j = 0;
print ++j; // Prints 1
print j; // Prints 1
Редагувати: Woops, повністю ігноруючи петлеву сторону речей. Немає фактичної різниці для циклів, коли це "крокова" частина (для (...; ...;)), але вона може вступати в гру в інших випадках.
Немає різниці, якщо ви не використовуєте значення після приросту в циклі.
for (int i = 0; i < 4; ++i){
cout<<i;
}
for (int i = 0; i < 4; i++){
cout<<i;
}
Обидві петлі будуть надруковані 0123.
Але різниця виникає, коли ви використовуєте значення після збільшення / зменшення в циклі, як показано нижче:
Попередня інкрементна петля:
for (int i = 0,k=0; i < 4; k=++i){
cout<<i<<" ";
cout<<k<<" ";
}
Вихід: 0 0 1 1 2 2 3 3
Петля збільшення розміру:
for (int i = 0, k=0; i < 4; k=i++){
cout<<i<<" ";
cout<<k<<" ";
}
Вихід: 0 0 1 0 2 1 3 2
Я сподіваюся, що різниця очевидна, порівнявши вихід. Тут слід зазначити, що приріст / декремент завжди виконується в кінці циклу for, і тому результати можна пояснити.
Ось Java-зразок та байт-код, пост- та попереднє збільшення не бачать різниці в байт-коді:
public class PreOrPostIncrement {
static int somethingToIncrement = 0;
public static void main(String[] args) {
final int rounds = 1000;
postIncrement(rounds);
preIncrement(rounds);
}
private static void postIncrement(final int rounds) {
for (int i = 0; i < rounds; i++) {
somethingToIncrement++;
}
}
private static void preIncrement(final int rounds) {
for (int i = 0; i < rounds; ++i) {
++somethingToIncrement;
}
}
}
А тепер для байт-коду (javap -private -c PreOrPostIncrement):
public class PreOrPostIncrement extends java.lang.Object{
static int somethingToIncrement;
static {};
Code:
0: iconst_0
1: putstatic #10; //Field somethingToIncrement:I
4: return
public PreOrPostIncrement();
Code:
0: aload_0
1: invokespecial #15; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: sipush 1000
3: istore_1
4: sipush 1000
7: invokestatic #21; //Method postIncrement:(I)V
10: sipush 1000
13: invokestatic #25; //Method preIncrement:(I)V
16: return
private static void postIncrement(int);
Code:
0: iconst_0
1: istore_1
2: goto 16
5: getstatic #10; //Field somethingToIncrement:I
8: iconst_1
9: iadd
10: putstatic #10; //Field somethingToIncrement:I
13: iinc 1, 1
16: iload_1
17: iload_0
18: if_icmplt 5
21: return
private static void preIncrement(int);
Code:
0: iconst_0
1: istore_1
2: goto 16
5: getstatic #10; //Field somethingToIncrement:I
8: iconst_1
9: iadd
10: putstatic #10; //Field somethingToIncrement:I
13: iinc 1, 1
16: iload_1
17: iload_0
18: if_icmplt 5
21: return
}
Так, є. Різниця полягає у зворотному значенні. Повернене значення "++ i" буде значенням після збільшення i. Повернення "i ++" буде значенням до збільшення. Це означає, що код виглядає наступним чином:
int a = 0;
int b = ++a; // a is incremented and the result after incrementing is saved to b.
int c = a++; // a is incremented again and the result before incremening is saved to c.
Отже, a було б 2, а b і c були б 1.
Я можу переписати такий код:
int a = 0;
// ++a;
a = a + 1; // incrementing first.
b = a; // setting second.
// a++;
c = a; // setting first.
a = a + 1; // incrementing second.
Фактичної різниці в обох випадках немає ", i
" збільшуватиметься на 1.
Але є різниця, коли ви використовуєте його у виразі, наприклад:
int i = 1;
int a = ++i;
// i is incremented by one and then assigned to a.
// Both i and a are now 2.
int b = i++;
// i is assigned to b and then incremented by one.
// b is now 2, and i is now 3
Існує більше для ++ i та i ++, ніж циклі та відмінності в продуктивності. ++ я повертає значення l, а i ++ повертає r-значення. Виходячи з цього, є багато речей, які ви можете зробити (++ i), але не (i ++).
1- It is illegal to take the address of post increment result. Compiler won't even allow you.
2- Only constant references to post increment can exist, i.e., of the form const T&.
3- You cannot apply another post increment or decrement to the result of i++, i.e., there is no such thing as I++++. This would be parsed as ( i ++ ) ++ which is illegal.
4- When overloading pre-/post-increment and decrement operators, programmers are encouraged to define post- increment/decrement operators like:
T& operator ++ ( )
{
// logical increment
return *this;
}
const T operator ++ ( int )
{
T temp( *this );
++*this;
return temp;
}
Мені змушує міркувати, чому так люди можуть записати вираження приросту в for-loop як i ++.
У циклі for-циклу, коли 3-й компонент є простим твердженням збільшення, як у
for (i=0; i<x; i++)
або
for (i=0; i<x; ++i)
різниці в отриманих стратах немає.
Як говорить @Jon B , в циклі for немає різниці.
Але в циклі while
або do...while
, ви можете знайти деякі відмінності, якщо ви робите порівняння з ++i
абоi++
while(i++ < 10) { ... } //compare then increment
while(++i < 10) { ... } //increment then compare
У javascript через наступне i ++ може бути краще використовувати:
var i=1;
alert(i++); // before, 1. current, 1. after, 2.
alert(i); // before, 2. current, 2. after, 2.
alert(++i); // before, 2. current, 3 after, 3.
Хоча масиви (я думаю, що всі) та деякі інші функції та виклики використовують 0 як вихідну точку, вам доведеться встановити i до -1, щоб цикл працював із масивом при використанні ++ i .
При використанні i ++ наступне значення використовуватиме збільшене значення. Можна сказати, що я ++ - це спосіб підрахунку людей, тому що ви можете почати з 0 .
Щоб зрозуміти, що робить цикл FOR
Зображення вище показує, що FOR може бути перетворений у WHILE , оскільки вони зрештою мають абсолютно однаковий код складання (принаймні, у gcc). Таким чином, ми можемо розбити FOR на пару частин, щоб зробити те, що він робить.
for (i = 0; i < 5; ++i) {
DoSomethingA();
DoSomethingB();
}
дорівнює версії WHILE
i = 0; //first argument (a statement) of for
while (i < 5 /*second argument (a condition) of for*/) {
DoSomethingA();
DoSomethingB();
++i; //third argument (another statement) of for
}
Це означає, що ви можете використовувати FOR як просту версію WHILE :
Перший аргумент FOR (int i) виконується зовні, перед циклом.
Третій аргумент FOR (i ++ або ++ i) виконується всередині в останньому рядку циклу.
TL: DR: незалежно від того,
i++
чи++i
ми знаємо, що, коли вони є окремими, вони не мають нічого іншого, окрім +1.У школі вони зазвичай вчать спосіб i ++, але також багато людей віддають перевагу способу ++ i з кількох причин .
ПРИМІТКА: Раніше i ++ мала дуже малий вплив на продуктивність, оскільки вона не тільки плюс одна сама по собі, але й зберігає початкове значення в реєстрі. Але наразі це не має ніякого значення, оскільки компілятор робить плюс одну частину однаковою.
Для петель може бути різниця. Це практичне застосування після / попереднього збільшення.
int i = 0;
while(i++ <= 10) {
Console.Write(i);
}
Console.Write(System.Environment.NewLine);
i = 0;
while(++i <= 10) {
Console.Write(i);
}
Console.ReadLine();
У той час як перший вважається 11, а петлі - 11 разів, другий - ні.
Переважно це скоріше використовується у простий час (x--> 0); - - Цикл для ітерації, наприклад, усіх елементів масиву (тут не застосовуються foreach-конструкції).
Вони обидва збільшують число. ++i
еквівалентно i = i + 1
.
i++
і ++i
дуже схожі, але не зовсім однакові. Обидва збільшують число, але ++i
збільшують число перед поточним виразом, тоді як i++
приріст - число після вираження.
int i = 3;
int a = i++; // a = 3, i = 4
int b = ++a; // b = 4, a =
Перевірте це посилання .
Так, є різниця між ++i
і i++
в for
циклі, хоча у незвичних випадках використання; коли змінна циклу з оператором збільшення / зменшення використовується в блоці для блоку або в виразі тестового циклу , або з однією зі змінних циклу . Ні, це не просто річ синтаксису.
Як і i
в коді, означає оцінити вираз, i
і оператор означає не оцінку, а просто операцію;
++i
означає значення приросту i
на 1 і пізніше оцінюють i
,i++
означає оцінку i
та пізніший приріст значення i
на 1.Отже, те, що отримується з кожного двох виразів, відрізняється тим, що те, що оцінюється, відрізняється в кожному. Все те ж саме для --i
іi--
Наприклад;
let i = 0
i++ // evaluates to value of i, means evaluates to 0, later increments i by 1, i is now 1
0
i
1
++i // increments i by 1, i is now 2, later evaluates to value of i, means evaluates to 2
2
i
2
У звичайних випадках використання, однак наступний приклад здається корисним чи не має значення, він показує різницю
for(i=0, j=i; i<10; j=++i){
console.log(j, i)
}
for(i=0, j=i; i<10; j=i++){
console.log(j, i)
}
Для i
визначених користувачем типів ці оператори могли (але не повинні) ) мати суттєво різну сематику в контексті циклу, і це може (але не повинно) впливати на поведінку описаного циклу.
Також, c++
як правило, найбезпечніше використовувати форму попереднього збільшення ( ++i
), оскільки вона легше оптимізується. (Скотт Ленгем побив мене до цього примху . Проклявай тебе, Скотт)
Я не знаю для інших мов, але в Java ++ i - приріст префікса, що означає: збільшити i на 1, а потім використовувати нове значення i у виразі, в якому я перебуває, а i ++ - приріст постфікса, що означає наступне : використовувати поточне значення i в виразі, а потім збільшити його на 1. Приклад:
public static void main(String [] args){
int a = 3;
int b = 5;
System.out.println(++a);
System.out.println(b++);
System.out.println(b);
}, а вихід: