W C jaka jest różnica między używaniem ++i
i i++
, a które należy stosować w bloku inkrementacji for
pętli?
W C jaka jest różnica między używaniem ++i
i i++
, a które należy stosować w bloku inkrementacji for
pętli?
Odpowiedzi:
++i
zwiększy wartość i
, a następnie zwróci wartość zwiększoną.
i = 1;
j = ++i;
(i is 2, j is 2)
i++
zwiększy wartość i
, ale zwróci pierwotną wartość, która była i
przechowywana przed zwiększeniem.
i = 1;
j = i++;
(i is 2, j is 1)
W przypadku for
pętli albo działa. ++i
wydaje się bardziej powszechny, być może dlatego, że właśnie tego używa się w K&R .
W każdym razie postępuj zgodnie z wytyczną „preferuj ++i
ponad i++
”, aby się nie pomylić.
Jest kilka komentarzy dotyczących wydajności ++i
i i++
. W żadnym kompilatorze nie będącym studentem nie będzie różnicy w wydajności. Możesz to sprawdzić, patrząc na wygenerowany kod, który będzie identyczny.
Pytanie o wydajność jest interesujące ... oto moja próba odpowiedzi: Czy istnieje różnica w wydajności między i ++ a ++ i w C?
Jak zauważa @OnFreund , dla obiektu C ++ jest inaczej, ponieważ operator++()
jest to funkcja, a kompilator nie może wiedzieć, jak zoptymalizować tworzenie obiektu tymczasowego w celu przechowywania wartości pośredniej.
for(int i=0; i<10; i++){ print i; }
czy to nie będzie inaczej niż w for(int i=0; i<10; ++i){ print i; }
moim rozumieniu, że niektóre języki dają różne wyniki w zależności od tego, którego używasz.
i++
ponieważ ma on postać „operand-operator”, a la przypisanie „operand-operator-wartość”. Innymi słowy, operand docelowy znajduje się po lewej stronie wyrażenia, tak jak w instrukcji przypisania.
i++
i print i
są w różnych instrukcjach, ale dlatego i++;
i i<10
są. Uwaga @ jonnyflash nie jest poza bazą. Załóżmy, że masz for(int i=0; i++<10){ print i; }
i for(int i=0; ++i<10){ print i; }
. Będą one działać inaczej w sposób opisany przez @johnnyflash w pierwszym komentarzu.
i ++ jest znany jako Post Increment, podczas gdy ++ i nazywa się Pre Increment.
i++
i++
jest przyrostowy, ponieważ zwiększa i
wartość o 1 po zakończeniu operacji.
Zobaczmy następujący przykład:
int i = 1, j;
j = i++;
Tutaj wartość j = 1
ale i = 2
. Tutaj wartość i
zostanie przypisana jako j
pierwsza, a następnie i
będzie zwiększana.
++i
++i
jest przyrostem wstępnym, ponieważ zwiększa i
wartość o 1 przed operacją. Oznacza to, j = i;
że wykona się po i++
.
Zobaczmy następujący przykład:
int i = 1, j;
j = ++i;
Tutaj wartość j = 2
ale i = 2
. Tutaj wartość i
zostanie przypisana j
po i
inkrementacji i
. Podobnie ++i
będzie wcześniej wykonane j=i;
.
Na pytanie, które należy zastosować w bloku inkrementacji pętli for? odpowiedź brzmi: możesz użyć dowolnego ... nie ma znaczenia. Wykona twoją pętlę dla tego samego nr. czasów.
for(i=0; i<5; i++)
printf("%d ",i);
I
for(i=0; i<5; ++i)
printf("%d ",i);
Obie pętle wytwarzają tę samą moc wyjściową. tj 0 1 2 3 4
.
Ma to znaczenie tylko wtedy, gdy go używasz.
for(i = 0; i<5;)
printf("%d ",++i);
W takim przypadku wyjście będzie 1 2 3 4 5
.
Nie martw się o „wydajność” (szybkość, naprawdę), która z nich jest szybsza. Obecnie mamy kompilatory, które zajmują się tymi sprawami. Używaj tych, które mają sens, na podstawie których wyraźniej widać twoje zamiary.
operator++(int)
(wersja postfiksowa) kod prawie musi utworzyć tymczasowy, który zostanie zwrócony. Czy jesteś pewien, że kompilatory zawsze mogą to zoptymalizować?
++i
zwiększa wartość, a następnie zwraca ją.
i++
zwraca wartość, a następnie ją zwiększa.
To subtelna różnica.
Użyj pętli for ++i
, ponieważ jest ona nieco szybsza. i++
utworzy dodatkową kopię, która zostanie po prostu wyrzucona.
i++
: W tym scenariuszu najpierw przypisywana jest wartość, a następnie następuje przyrost.
++i
: W tym scenariuszu najpierw wykonywany jest przyrost, a następnie przypisywana jest wartość
Poniżej znajduje się wizualizacja obrazu, a także tutaj jest miły praktyczny film, który pokazuje to samo.
Przyczyna ++i
może być nieco szybsza niż i++
to, że i++
może wymagać lokalnej kopii wartości i, zanim zostanie zwiększona, a ++i
nigdy tego nie robi. W niektórych przypadkach niektóre kompilatory zoptymalizują go, jeśli to możliwe ... ale nie zawsze jest to możliwe i nie wszystkie kompilatory to robią.
Staram się nie polegać zbytnio na optymalizacjach kompilatorów, więc postępuję zgodnie z radą Ryana Foxa: kiedy mogę korzystać z obu, używam ++i
.
i
niż o wartości 1 podczas pisania instrukcji 1;
.
Efektywny wynik użycia jednej z pętli jest identyczny. Innymi słowy, pętla zrobi dokładnie to samo w obu przypadkach.
Pod względem wydajności może wystąpić kara związana z wyborem i ++ zamiast ++ i. Jeśli chodzi o specyfikację języka, użycie operatora post-increment powinno stworzyć dodatkową kopię wartości, na którą działa operator. Może to być źródłem dodatkowych operacji.
Należy jednak wziąć pod uwagę dwa główne problemy z poprzednią logiką.
Nowoczesne kompilatory są świetne. Wszystkie dobre kompilatory są wystarczająco inteligentne, aby zdać sobie sprawę z tego, że widzi przyrost liczby całkowitej w pętli for, i zoptymalizuje obie metody do tego samego wydajnego kodu. Jeśli użycie przyrostu powyżej przyrostu faktycznie powoduje, że Twój program działa wolniej, oznacza to, że używasz strasznego kompilatora.
Pod względem złożoności operacyjnej dwie metody (nawet jeśli kopia jest faktycznie wykonywana) są równoważne. Liczba instrukcji wykonywanych wewnątrz pętli powinna znacznie zdominować liczbę operacji w operacji przyrostowej. Dlatego w każdej pętli o znacznych rozmiarach metoda inkrementacji zostanie znacznie przyćmiona przez wykonanie korpusu pętli. Innymi słowy, lepiej jest martwić się o optymalizację kodu w pętli niż o przyrost.
Moim zdaniem cała kwestia sprowadza się po prostu do preferencji stylu. Jeśli uważasz, że wzrost wstępny jest bardziej czytelny, skorzystaj z niego. Osobiście wolę post-increment, ale prawdopodobnie dlatego, że tego się nauczyłem, zanim dowiedziałem się czegoś o optymalizacji.
Jest to kwintesencyjny przykład przedwczesnej optymalizacji, a takie problemy mogą odciągnąć nas od poważnych problemów projektowych. Jest to jednak dobre pytanie, ponieważ nie ma jednorodności w stosowaniu ani konsensusu w kwestii „najlepszych praktyk”.
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.
Przykład:
int i = 1;
int x = i++; //x is 1, i is 2
int y = ++i; //y is 3, i is 3
++i
(Praca prefiks) Przyrosty a następnie przydziela wartość
(na przykład) int i = 5
, int b = ++i
w tym przypadku, 6 przyporządkowany jest w pozycji b, a następnie stopniowo, aby 7 i tak dalej.
i++
(Praca Postfix) wyznacza i następnie zwiększa wartość
(na przykład) int i = 5
, int b = i++
w tym przypadku 5 przyporządkowany jest w pozycji b, a następnie stopniowo, aby 6 i tak dalej.
Przyrost pętli for: i++
jest najczęściej używany, ponieważ zwykle używamy wartości początkowej i
przed inkrementacją pętli for. Ale w zależności od logiki programu może się różnić.
Zakładam, że rozumiesz teraz różnicę w semantyce (choć szczerze mówiąc, zastanawiam się, dlaczego ludzie pytają „co oznacza operator X” na temat przepełnienia stosu zamiast czytać, no wiesz, książkę lub tutorial internetowy lub coś w tym rodzaju.
Ale w każdym razie, o ile użyć, ignoruj pytania dotyczące wydajności, które są mało prawdopodobne nawet w C ++. Jest to zasada, którą powinieneś zastosować, decydując, którego użyć:
Powiedz, co masz na myśli w kodzie.
Jeśli nie potrzebujesz przyrostu wartości przed wyciągiem, nie używaj tej formy operatora. Jest to drobny problem, ale jeśli nie pracujesz z przewodnikiem po stylu, który zbanuje jedną wersję na korzyść drugiej (aka przewodnik po stylu z kośćmi), powinieneś użyć formularza, który najlepiej wyraża to, co próbujesz zrobić.
QED, użyj wersji wstępnej:
for (int i = 0; i != X; ++i) ...
Różnicę można zrozumieć za pomocą tego prostego kodu C ++ poniżej:
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;
Główną różnicą jest
- i ++ Post ( po zwiększeniu ) i
++ i Pre ( przed przyrostem )
- post, jeśli
i =1
przyrosty pętli jak1,2,3,4,n
- pre, jeśli
i =1
przyrosty pętli jak2,3,4,5,n
Ten mały kod może pomóc w wizualizacji różnicy pod innym kątem niż już opublikowane odpowiedzi:
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);
Wynik jest następujący:
//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
Zwróć uwagę na sytuacje przed i po.
Jeśli chodzi o to, który z nich powinien zostać użyty w bloku inkrementacyjnym pętli for, myślę, że najlepszym sposobem na podjęcie decyzji jest dobry przykład:
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);
Wynik jest następujący:
> iteration #0 > iteration #1 > iteration #2 > iteration #3
> iteration #0 > iteration #1 > iteration #2 > iteration #3
Nie wiem o tobie, ale nie widzę żadnej różnicy w jego użyciu, przynajmniej w pętli for.
Poniższy fragment kodu C ilustruje różnicę między operatorami inkrementacji i dekrementacji przed i po:
int i;
int j;
Operatorzy przyrostowi:
i = 1;
j = ++i; // i is now 2, j is also 2
j = i++; // i is now 3, j is 2
Wstępne tworzenie oznacza przyrost na tej samej linii. Post-increment oznacza przyrost po wykonaniu linii.
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
Jeśli chodzi o operatory OR, AND, staje się bardziej interesujące.
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
}
W szyku
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
}
W C ++ post / wstępna inkrementacja zmiennej wskaźnika
#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)
}
Wkrótce:
++i
i i++
działa tak samo, jeśli nie piszesz ich w funkcji. Jeśli użyjesz czegoś podobnego function(i++)
lub function(++i)
zauważysz różnicę.
function(++i)
mówi pierwszy przyrost i o 1, a następnie wstaw to i
do funkcji z nową wartością.
function(i++)
mówi najpierw wstaw i
do funkcji po tym zwiększeniu i
o 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
int j = ++i;
a int k = i++;
nawet wtedy, gdy nie ma w tym wywołania funkcji.
Jedyną różnicą jest kolejność operacji między przyrostem zmiennej a wartością zwracaną przez operatora.
Ten kod i jego dane wyjściowe wyjaśniają różnicę:
#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);
}
Dane wyjściowe to:
i before: 1; value returned by i++: 0, i after: 1
i before: 1; value returned by ++i: 1, i after: 1
Zasadniczo ++i
zwraca więc wartość po jej zwiększeniu, a ++i
zwraca wartość przed jej zwiększeniem. Na koniec w obu przypadkach wartość i
zostanie zwiększona.
Inny przykład:
#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;
}
Wynik:
i=0, i++*2=0
i=0, ++i*2=2
i=0, (++i)*2=2
i=0, (++i)*2=2
Różnice są jasne, gdy wartość zwracana jest przypisany do innej zmiennej lub gdy przyrost przeprowadza się łączenie z innych operacji, w których pierwszeństwo jest zastosowanie operacji ( i++*2
różni się od ++i*2
, ale (i++)*2
i (++i)*2
powraca do tej samej wartości), w wielu przypadkach stosować zamiennie. Klasycznym przykładem jest składnia pętli for:
for(int i=0; i<10; i++)
ma taki sam efekt jak
for(int i=0; i<10; ++i)
Aby nie wprowadzać zamieszania między dwoma operatorami, przyjąłem tę zasadę:
Skojarz pozycję operatora ++
w odniesieniu do zmiennej i
z kolejnością ++
operacji w odniesieniu do przypisania
Innymi słowy:
++
przed i
środkami należy dokonać przyrostu przed przypisaniem;++
po i
środkach należy wykonać inkrementację po przypisaniu:Możesz myśleć o wewnętrznej konwersji tego jako o wielu instrukcjach ;
i++;
możesz myśleć tak,
i;
i = i+1;
++i;
możesz myśleć tak,
i = i+i;
i;
a = i ++ oznacza a zawiera bieżącą wartość i a = ++ i oznacza a zawiera przyrostową wartość i
a = i++;
oznacza, że przechowywana wartość a
będzie wartością i
przed inkrementem, ale „bez inkrementacji” oznacza, że i
nie jest inkrementowana, co jest całkowicie błędne - i
jest inkrementowana, ale wartość wyrażenia jest wartością przed inkrementacją.
Oto przykład, aby zrozumieć różnicę
int i=10;
printf("%d %d",i++,++i);
wyjście: 10 12/11 11
(w zależności od kolejności oceny argumentów printf
funkcji, która różni się w zależności od kompilatora i architektury)
Objaśnienie:
i++
-> i
jest drukowane, a następnie przyrosty. (Drukuje 10, ale i
stanie się 11)
++i
-> i
zwiększa wartość i drukuje wartość. (Drukuje 12, a także wartość i
12)
i++
i++i