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?
c
for-loop
post-increment
pre-increment
The.Anti.9
źródło
źródło
Odpowiedzi:
++i
zwiększy wartośći
, a następnie zwróci wartość zwiększoną.i++
zwiększy wartośći
, ale zwróci pierwotną wartość, która byłai
przechowywana przed zwiększeniem.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
ponadi++
”, aby się nie pomylić.Jest kilka komentarzy dotyczących wydajności
++i
ii++
. 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.źródło
for(int i=0; i<10; i++){ print i; }
czy to nie będzie inaczej niż wfor(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++
iprint i
są w różnych instrukcjach, ale dlategoi++;
ii<10
są. Uwaga @ jonnyflash nie jest poza bazą. Załóżmy, że maszfor(int i=0; i++<10){ print i; }
ifor(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ększai
wartość o 1 po zakończeniu operacji.Zobaczmy następujący przykład:
Tutaj wartość
j = 1
alei = 2
. Tutaj wartośći
zostanie przypisana jakoj
pierwsza, a następniei
będzie zwiększana.++i
++i
jest przyrostem wstępnym, ponieważ zwiększai
wartość o 1 przed operacją. Oznacza to,j = i;
że wykona się poi++
.Zobaczmy następujący przykład:
Tutaj wartość
j = 2
alei = 2
. Tutaj wartośći
zostanie przypisanaj
poi
inkrementacjii
. Podobnie++i
będzie wcześniej wykonanej=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.
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.
W takim przypadku wyjście będzie
1 2 3 4 5
.źródło
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.
źródło
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.źródło
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.
źródło
Przyczyna
++i
może być nieco szybsza niżi++
to, żei++
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
.źródło
i
niż o wartości 1 podczas pisania instrukcji1;
.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”.
źródło
Obaj zwiększają liczbę.
++i
jest równoważne zi = 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 gdyi++
zwiększają liczbę po obliczeniu wyrażenia.Przykład:
źródło
++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ątkoweji
przed inkrementacją pętli for. Ale w zależności od logiki programu może się różnić.źródło
++i
: jest inkrementem, drugi jest inkrementem.i++
: pobiera element, a następnie zwiększa go.++i
: inkrementuje i, a następnie zwraca element.Przykład:
Wynik:
źródło
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:
źródło
Różnicę można zrozumieć za pomocą tego prostego kodu C ++ poniżej:
źródło
źródło
i ++ i ++ i
Ten mały kod może pomóc w wizualizacji różnicy pod innym kątem niż już opublikowane odpowiedzi:
Wynik jest następujący:
Zwróć uwagę na sytuacje przed i po.
dla pętli
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:
Wynik jest następujący:
Nie wiem o tobie, ale nie widzę żadnej różnicy w jego użyciu, przynajmniej w pętli for.
źródło
Poniższy fragment kodu C ilustruje różnicę między operatorami inkrementacji i dekrementacji przed i po:
Operatorzy przyrostowi:
źródło
Wstępne tworzenie oznacza przyrost na tej samej linii. Post-increment oznacza przyrost po wykonaniu linii.
Jeśli chodzi o operatory OR, AND, staje się bardziej interesujące.
W szyku
W C ++ post / wstępna inkrementacja zmiennej wskaźnika
źródło
Wkrótce:
++i
ii++
działa tak samo, jeśli nie piszesz ich w funkcji. Jeśli użyjesz czegoś podobnegofunction(i++)
lubfunction(++i)
zauważysz różnicę.function(++i)
mówi pierwszy przyrost i o 1, a następnie wstaw toi
do funkcji z nową wartością.function(i++)
mówi najpierw wstawi
do funkcji po tym zwiększeniui
o 1.źródło
int j = ++i;
aint 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ę:
Dane wyjściowe to:
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:
Wynik:
Wiele razy nie ma różnicy
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:ma taki sam efekt jak
Reguła do zapamiętania
Aby nie wprowadzać zamieszania między dwoma operatorami, przyjąłem tę zasadę:
Skojarz pozycję operatora
++
w odniesieniu do zmienneji
z kolejnością++
operacji w odniesieniu do przypisaniaInnymi słowy:
++
przedi
środkami należy dokonać przyrostu przed przypisaniem;++
poi
środkach należy wykonać inkrementację po przypisaniu:źródło
Możesz myśleć o wewnętrznej konwersji tego jako o wielu instrukcjach ;
możesz myśleć tak,
możesz myśleć tak,
źródło
a = i ++ oznacza a zawiera bieżącą wartość i a = ++ i oznacza a zawiera przyrostową wartość i
źródło
a = i++;
oznacza, że przechowywana wartośća
będzie wartościąi
przed inkrementem, ale „bez inkrementacji” oznacza, żei
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ę
wyjście:
10 12/11 11
(w zależności od kolejności oceny argumentówprintf
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, alei
stanie się 11)++i
->i
zwiększa wartość i drukuje wartość. (Drukuje 12, a także wartośći
12)źródło
i++
i++i