Czy jest jakaś różnica w ++i
i i++
w for
pętli? Czy to po prostu składnia?
Czy jest jakaś różnica w ++i
i i++
w for
pętli? Czy to po prostu składnia?
Odpowiedzi:
++ jest znany jako postfix.
dodać 1 do a, zwraca starą wartość.
++ a jest znany jako przedrostek.
dodać 1 do a, zwraca nową wartość.
DO#:
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++);
}
Wynik:
1
2
3
4
0
1
2
3
foreach
a while
pętle zależą od używanego typu przyrostu. Z pętlami jak poniżej nie ma znaczenia, ponieważ nie używasz wartości zwracanej przez 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
Jeśli zastosowana zostanie wartość oszacowana, wówczas rodzaj przyrostu będzie znaczący:
int n = 0;
for (int i = 0; n < 5; n = i++) { }
Pre-inkrement ++ i inkrementuje wartość i i ocenia do nowej wartości inkrementowanej.
int i = 3;
int preIncrementResult = ++i;
Assert( preIncrementResult == 4 );
Assert( i == 4 );
Post-inkrementacja i ++ inkrementuje wartość i i ocenia do pierwotnej nierekrementowanej wartości.
int i = 3;
int postIncrementResult = i++;
Assert( postIncrementtResult == 3 );
Assert( i == 4 );
W C ++ przyrost jest zwykle preferowany tam, gdzie możesz użyć jednego z nich.
Wynika to z faktu, że jeśli użyjesz funkcji przyrostowej, kompilator może wymagać wygenerowania kodu, który tworzy dodatkową zmienną tymczasową. Wynika to z faktu, że zarówno poprzednia, jak i nowa wartość inkrementowanej zmiennej muszą być gdzieś przechowywane, ponieważ mogą być potrzebne w innym miejscu w ocenianym wyrażeniu.
Tak więc przynajmniej w C ++ może występować różnica w wydajności, która poprowadzi twój wybór, którego użyć.
Jest to głównie problem tylko wtedy, gdy inkrementowana zmienna jest typem zdefiniowanym przez użytkownika z przesłoniętym operatorem ++. W przypadku typów pierwotnych (int itp.) Nie ma różnicy w wydajności. Warto jednak pozostać przy operatorze wstępnego zwiększania jako wskazówkę, chyba że operator po zwiększeniu jest zdecydowanie wymagany.
Tutaj jest trochę więcej dyskusji:
https://web.archive.org/web/20170405054235/http://en.allexperts.com/q/C-1040/Increment-operators.htm
W C ++, jeśli używasz STL, możesz używać pętli z iteratorami. Te mają głównie przesłonięte operatory ++, więc trzymanie się wstępnego przyrostu jest dobrym pomysłem. Kompilatory cały czas stają się inteligentniejsze, a nowsze mogą być w stanie przeprowadzić optymalizacje, co oznacza, że nie ma różnicy w wydajności - szczególnie jeśli typ inkrementowany jest zdefiniowany w pliku nagłówkowym (jak często są implementacje STL), dzięki czemu kompilator może zobaczyć, jak metoda jest zaimplementowana, a następnie może wiedzieć, jakie optymalizacje są bezpieczne do wykonania. Mimo to prawdopodobnie nadal warto trzymać się wstępnego przyrostu, ponieważ pętle są wykonywane wiele razy, a to oznacza, że niewielka kara za wydajność może wkrótce zostać zwiększona.
W innych językach, takich jak C #, w których operator ++ nie może być przeciążony, nie ma różnicy w wydajności. Używane w pętli do przesuwania zmiennej pętli, operatory przyrostowe przed i po są równoważne.
Poprawka: przeciążenie ++ w języku C # jest dozwolone. Wydaje się jednak, że w porównaniu do C ++ w języku c # nie można przeciążać wersji wstępnej i końcowej niezależnie. Zakładam więc, że jeśli wynik wywołania ++ w C # nie zostanie przypisany do zmiennej lub użyty jako część złożonego wyrażenia, to kompilator zredukuje wersje przed i po ++ do kodu, który działa równorzędnie.
W języku C # nie ma różnicy, gdy jest używany w pętli for .
for (int i = 0; i < 10; i++) { Console.WriteLine(i); }
wypisuje to samo co
for (int i = 0; i < 10; ++i) { Console.WriteLine(i); }
Jak zauważyli inni, kiedy używa się ogólnie i ++ i ++, mam subtelną, ale znaczącą różnicę:
int i = 0;
Console.WriteLine(i++); // Prints 0
int j = 0;
Console.WriteLine(++j); // Prints 1
i ++ odczytuje wartość i, a następnie ją zwiększa.
++ i zwiększa wartość i, a następnie ją odczytuje.
++i
i i++
wykonaj te same operacje w tej samej kolejności: utwórz tymczasową kopię i
; zwiększyć wartość temp, aby uzyskać nową wartość (nie zastępować temp); zapisz nową wartość w i
; teraz, jeśli ++i
wynik jest zwracany, to nowa wartość; jeśli i++
wynik jest zwracany, to kopia tymczasowa. Bardziej szczegółowa odpowiedź tutaj: stackoverflow.com/a/3346729/3330348
Pytanie brzmi:
Czy istnieje różnica w ++ i i ++ w pętli for?
Odpowiedź brzmi: nie .
Dlaczego każda inna odpowiedź musi zawierać szczegółowe objaśnienia dotyczące inkrementacji przed i po, gdy nie jest to nawet zadawane?
Ta pętla for:
for (int i = 0; // Initialization
i < 5; // Condition
i++) // Increment
{
Output(i);
}
Przetłumaczyłbym na ten kod bez użycia pętli:
int i = 0; // Initialization
loopStart:
if (i < 5) // Condition
{
Output(i);
i++ or ++i; // Increment
goto loopStart;
}
Czy ma to teraz znaczenie, czy umieścisz tutaj, i++
czy ++i
jako przyrost? Nie, nie dlatego, że wartość zwracana operacji przyrostowej jest nieznaczna. i
będzie zwiększany PO wykonaniu kodu znajdującego się w treści pętli for.
Ponieważ pytasz o różnicę w pętli, myślę, że masz na myśli
for(int i=0; i<10; i++)
...;
W takim przypadku nie ma różnicy w większości języków: Pętla zachowuje się tak samo, niezależnie od tego, czy piszesz i++
i ++i
. W C ++ możesz pisać własne wersje operatorów ++ i możesz zdefiniować dla nich osobne znaczenia, jeśli i
jest to typ zdefiniowany przez użytkownika (na przykład twoja własna klasa).
Powodem, dla którego nie ma to znaczenia powyżej, jest to, że nie używasz wartości i++
. Inną rzeczą jest, kiedy to robisz
for(int i=0, a = 0; i<10; a = i++)
...;
Teraz jest różnica, ponieważ, jak podkreślają inni, i++
oznacza przyrost, ale oszacowanie do poprzedniej wartości , ale ++i
oznacza przyrost, ale oszacowanie doi
(w ten sposób oszacowałoby do nowej wartości). W powyższym przypadku a
przypisana jest poprzednia wartość i, natomiast i jest zwiększane.
Jak pokazuje ten kod (patrz nieszyfrowany MSIL w komentarzach), kompilator C # 3 nie wprowadza rozróżnienia między i ++ i ++ i w pętli for. Gdyby wziąć wartość i ++ lub ++ i, na pewno byłaby różnica (ta została skompilowana w 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++;
}
}
}
}
Jeden (++ i) to wzrost wstępny, jeden (i ++) to wzrost wstępny. Różnica polega na tym, jaka wartość jest natychmiast zwracana z wyrażenia.
// Psuedocode
int i = 0;
print i++; // Prints 0
print i; // Prints 1
int j = 0;
print ++j; // Prints 1
print j; // Prints 1
Edycja: Woops, całkowicie zignorowałem pętlę rzeczy. Nie ma żadnej rzeczywistej różnicy w przypadku pętli, gdy jest to część „krokowa” (dla (...; ...;)), ale może wejść w grę w innych przypadkach.
Nie ma różnicy, jeśli nie używasz wartości po inkrementacji w pętli.
for (int i = 0; i < 4; ++i){
cout<<i;
}
for (int i = 0; i < 4; i++){
cout<<i;
}
Obie pętle wydrukują 0123.
Ale różnica pojawia się, gdy używasz wartości po inkrementacji / dekrementacji w swojej pętli, jak poniżej:
Pętla przed przyrostem:
for (int i = 0,k=0; i < 4; k=++i){
cout<<i<<" ";
cout<<k<<" ";
}
Wyjście: 0 0 1 1 2 2 3 3
Pętla przyrostowa:
for (int i = 0, k=0; i < 4; k=i++){
cout<<i<<" ";
cout<<k<<" ";
}
Wyjście: 0 0 1 0 2 1 3 2
Mam nadzieję, że różnica jest wyraźna poprzez porównanie wyników. Należy zwrócić uwagę na to, że inkrementacja / dekrementacja jest zawsze wykonywana na końcu pętli for, a zatem wyniki można wyjaśnić.
Oto przykład Java, a kod bajtowy, post- i preIncrement nie pokazują żadnej różnicy w kodzie bajtowym:
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;
}
}
}
A teraz dla kodu bajtowego (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
}
Tak jest. Różnica polega na wartości zwracanej. Zwracana wartość „++ i” będzie wartością po inkrementacji i. Zwrot „i ++” będzie wartością przed inkrementacją. Oznacza to, że kod wygląda następująco:
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.
Dlatego a będzie równe 2, a b i c będą równe 1.
Mógłbym przepisać kod w ten sposób:
int a = 0;
// ++a;
a = a + 1; // incrementing first.
b = a; // setting second.
// a++;
c = a; // setting first.
a = a + 1; // incrementing second.
W obu przypadkach nie ma faktycznej różnicy ' i
' zostanie zwiększona o 1.
Ale jest różnica, gdy używasz go w wyrażeniu, na przykład:
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 ++ to coś więcej niż pętle i różnice w wydajności. ++ i zwraca wartość l, a i ++ zwraca wartość r. Na tej podstawie istnieje wiele rzeczy, które możesz zrobić (++ i), ale nie (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;
}
Zastanawia mnie, dlaczego ludzie mogą pisać wyrażenie przyrostowe w pętli for jako i ++.
W pętli for, gdy trzeci składnik jest prostą instrukcją przyrostową, jak w
for (i=0; i<x; i++)
lub
for (i=0; i<x; ++i)
nie ma różnicy w wynikowych wykonaniach.
Jak mówi @Jon B , nie ma różnicy w pętli for.
Ale w pętli while
lub do...while
możesz znaleźć pewne różnice, jeśli porównujesz z ++i
lubi++
while(i++ < 10) { ... } //compare then increment
while(++i < 10) { ... } //increment then compare
W javascript z powodu następującego i ++ może być lepiej użyć:
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.
Podczas gdy tablice (myślę, że wszystkie) oraz niektóre inne funkcje i wywołania używają 0 jako punktu początkowego, musiałbyś ustawić i na -1, aby pętla działała z tablicą podczas używania ++ i .
Podczas korzystania z i ++ następująca wartość wykorzysta zwiększoną wartość. Można powiedzieć, że i ++ to sposób, w jaki liczą się ludzie, ponieważ możesz zacząć od 0 .
Aby zrozumieć, co robi pętla FOR
Powyższy obraz pokazuje, że FOR można przekonwertować na WHILE , ponieważ ostatecznie mają one całkowicie ten sam kod asemblera (przynajmniej w gcc). Więc możemy rozbić FOR na kilka części, aby zrozumieć, co robi.
for (i = 0; i < 5; ++i) {
DoSomethingA();
DoSomethingB();
}
jest równa wersji 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
}
Oznacza to, że możesz użyć FOR jako prostej wersji WHILE :
Pierwszy argument FOR (int i) jest wykonywany na zewnątrz przed pętlą.
Trzeci argument FOR (i ++ lub ++ i) jest wykonywany wewnątrz w ostatnim wierszu pętli.
TL: DR: bez względu na to, czy
i++
lub++i
, wiemy, że gdy są samodzielne, nie mają znaczenia, ale dają +1 dla siebie.W szkole zwykle uczą języka i ++, ale jest też wiele osób, które preferują język i ++ z kilku powodów .
UWAGA: W przeszłości i ++ miał bardzo mały wpływ na wydajność, ponieważ nie tylko sam plus jeden, ale także zachowuje oryginalną wartość w rejestrze. Ale na razie nie ma znaczenia, ponieważ kompilator sprawia, że część dodatnia jest taka sama.
Różnice mogą występować w przypadku pętli. Jest to praktyczne zastosowanie post / przyrostu.
int i = 0;
while(i++ <= 10) {
Console.Write(i);
}
Console.Write(System.Environment.NewLine);
i = 0;
while(++i <= 10) {
Console.Write(i);
}
Console.ReadLine();
Podczas gdy pierwszy liczy się do 11 i zapętla 11 razy, drugi nie.
Najczęściej jest to używane raczej w krótkim czasie (x--> 0); - - Pętla iteruje na przykład wszystkie elementy tablicy (wyłączając tutaj konstrukcje foreach).
Obaj zwiększają liczbę. ++i
jest równoważne z i = i + 1
.
i++
i ++i
są bardzo podobne, ale nie dokładnie takie same. Oba zwiększają liczbę, ale ++i
zwiększają liczbę przed obliczeniem bieżącego wyrażenia, podczas gdy i++
zwiększają liczbę po obliczeniu wyrażenia.
int i = 3;
int a = i++; // a = 3, i = 4
int b = ++a; // b = 4, a =
Sprawdź ten link .
Tak, istnieje różnica pomiędzy ++i
i i++
w for
pętli, choć w nietypowych przypadkach użycia; gdy zmienna pętli z operatorem zwiększania / zmniejszania jest używana w bloku for lub w wyrażeniu testu pętli lub z jedną ze zmiennych pętli . Nie, nie jest to po prostu składnia.
Jak i
w kodzie oznacza wyrażenie, i
a operator nie oznacza oceny, a jedynie operację;
++i
oznacza wartość przyrostu o i
1 i późniejszą ocenę i
,i++
oznacza oszacowanie, i
a następnie zwiększenie wartości o i
1.To, co jest otrzymywane z każdego z dwóch wyrażeń, różni się, ponieważ to, co jest oceniane, różni się w każdym z nich. Wszystko to samo dla --i
ii--
Na przykład;
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
W nietypowych przypadkach, jednak następny przykład wydaje się przydatny lub nie ma znaczenia, pokazuje różnicę
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)
}
W przypadku i
typów zdefiniowanych przez użytkownika operatory te mogą (ale nie powinny ) mieć znacząco różne dane sematyczne w kontekście indeksu pętli, a to może (ale nie powinno) wpływać na zachowanie opisanej pętli.
Ponadto w c++
zasadzie najbezpieczniej jest używać formularza wstępnego przyrostu ( ++i
), ponieważ łatwiej go zoptymalizować. (Scott Langham pobił mnie na ten smakołyk . Przeklinam cię, Scott)
Dont wiem dla innych języków, ale w Javie ++ i jest prefiks przyrost który oznacza: zwiększenie I o 1, a następnie użyć nowej wartości I w wypowiedzi, w których I miejsce zamieszkania, a ja ++ jest przyrost postfix co oznacza, że następuje : użyj bieżącej wartości i w wyrażeniu, a następnie zwiększ ją o 1. Przykład:
public static void main(String [] args){
int a = 3;
int b = 5;
System.out.println(++a);
System.out.println(b++);
System.out.println(b);
}, a wynik to: