[C++] inkrementacja


(Kanaliaon) #1

Czy instrukcja i++ to to samo co i=i+1 ?


(Marcindodo) #2

tak :wink:

czyli i++ najpierw zwraca wartość i a potem dodaje + 1

a ++i najpierw zwiększa wartość i i zwraca już tą powiększoną


(Quentin) #3

Tak, przykładowo masz taki kod:

int i = 5;


cout << i++; // wyświetli się 5

cout << i; // teraz wyświetli się 6

Jeżeli mamy postinkrementację (i++) to wartością tego wyrażenia kiedy posługujemy się std::cout jest jeszcze stara wersja i - potem dopiero następuje zwiększenie o 1.

Oczywiście kiedy nie wypisujemy wartości wyrażenia na ekran, np. jako warunek pętli możemy podać zarówno i++ lub ++i - wtedy to nie ma znaczenia.


([alex]) #4

A jednak ma znaczenie. Po pierwsze w wyniku operacji ++i lub --i powstaje tak zwana lvalue - czyli właściwie referencja na zmienną i ; Oznacza to że bez problemu skompiluje się i zadziała:

cout<<++++++i; [/code]

zwiększy zmienną i o 3. Natomiast w wyniku operacji i++ oraz i-- powstaje stała, więc kod:

[code=php]cout<<i++ ++; 

nawet się nie skompiluje.

O ile zmienna i jest jakimś standardowym typem liczbowym to oczywiście osobno stojąca operacja ++i jest ty samym co i++ a --i jest tym samym co i-- (jedynie i++ oraz i-- nieco wolniej się kompilują, ale kto by liczył mikrosekundy podczas kompilacji która i tak przebiega najszybszej w kilka sekund), jednak kiedy zmienna i jest obiektem jakieś klasy to już jest olbrzymia różnica, i++ oraz i-- są o wiele gorsze. Jeżeli ktoś chcę to mogę tą różnicę wyjaśnić szczegółowo.

Podsumowując... o ile w jakimś konkretnym miejscu programu nie jest istotne którą z operacji użyć, przedrostkowej ( ++i oraz --i ) czy przyrostkowej ( i++ oraz i-- ) zawszę należy wybrać formę przedrostkową.


(Fiołek) #5

Ważne jest założenie, że i jest typu int/char/short bo jeśli sami to zaimplementujemy to będziemy tak mogli robić. Zresztą, kilkukrotne inkrementowanie w jednej instrukcji jest bez sensu. IMO niepotrzebny cukier składniowy. Lepie użyć operatora +=.

Nie do końca tak jest. Wszystko zależy od tego JAK zaimplementujemy operator przyrostkowy i przedrostkowy. Jeden może robić dużo, a drugi nic i to od nas zależy który wybierzemy. Oczywiście dobrym nawykiem jest trzymanie się konwencji, ale to nie jest wymagane.


([alex]) #6

Zapomniałeś o long/float/double/long long/unsigned ja to ująłem w kilku słowach standardowe typy liczbowe.

Wcale nie lepiej :stuck_out_tongue: ++++i; będzie szybsze od i+=2; , z tym zwiększaniem o 3 chodziło mi bardziej o łączenia typu (++i)<<=1 gdzie za pomocą += nic nie zrobisz, pokazałem na prostym przykładzie o co z tą lvalue chodzi.

Owszem można zaimplementować operator mnożenia który będzie odejmował, a operator przypisywania który będzie mnożył, ale gdzie w tym sens, kiedy autor postu dojdzie do przeciążania operatorów to sam bez problemu dojdzie do tego co napisałeś, a dopóki nie doszedł to moja rada mu pomoże a twoja o ile będzie próbować zrozumieć - tylko namiesza w głowie. Nie rozumiem poco czepiasz się z tymi bezsensownymi uwagami, posty nabijasz?


(Fiołek) #7

Masz rację, nie zauważyłem :wink:

Teraz babola walnąłeś :wink: ++++i będzie w najlepszym przypadku tak samo szybkie jak i += 2 bo i tak wszystko będzie się sprowadzać do add eax, 2, za eax może być inny rejestr lub adres. W release VC++ mi do tego oba przykłady skompilował. Więc szybkość tych dwóch instrukcji jest IDENTYCZNA.

Mówię o tym, bo nie lubię niedopowiedzeń. Wolę gdy mówi się o wszystkich(lub większości) kruczkach. Przypuszczam, że moja rada mu nie namiesza w głowie, tylko wyjaśni, że w C++ "wszystko jest możliwe".

Teraz to ty się bezsensownie "czepiasz", posty nabijasz? :wink:


([alex]) #8

Więc wyłącz optymalizacje w tym VC++ i będzie szybciej, porządne kompilatory po obie te instrukcji optymalizują do dwóch inc eax a bez optymalizacji (w porządnych kompilatorach) ++++i szybsze, zanim walniesz kolejnego babola, może najpierw sprawdź pisząc krótkiego testa w assembler'ze, albo poczytaj jakie są czasy wykonania tych instrukcji.

Twoi wypowiedzi zabrzmiały tak jakby się nie zgadzałeś z tym co napisałem. W sumie sprowadza się to do tego, że albo lepiej użyć przedrostkowej formy (no chyba że algorytm wymaga) albo ta klasa napisana bez sensu i lepiej wcale jej nie używać. Co do teoretycznych możliwości to rzeczywiście można zaimplementować jakąś klasę tak że przedrostkowa forma będzie sto albo nawet milion razy wolniejsza od przyrostkowej.


(Fiołek) #9

Teraz mi powiedz, co będzie szybsze:

inc eax

inc eax

inc eax

inc eax

* czy

add eax, 4

**?

To te Twoje "optymalizacje" raczej spowolnią kod. Podsumowując inc jest szybszy tylko dla 2 wywołań, przy 3 jest tak samo szybki jak add, a przy większej ilości jest wolniejszy od add.

* - wg Twojej tabelki cztery cykle.

** - wg Twojej tabelki trzy cykle.


([alex]) #10

Już przy zwiększaniu o 3 instrukcje inc ma sens zamienić na add , owszem czas ten sam, zaś wersja z add zajmuje mniej miejsca w pamięci :smiley: Oczywiste chyba jest że im więcej tych operacji inc tym bardziej się opłaca zamienić na add. Jednak przy zwiększaniu o 1 lub o 2 inc jest lepsza niż add. Poza tym chciał bym zobaczyć jak w następującym kodzie zamienisz ++++i na i+=2 :stuck_out_tongue:

#include 
#include 
#include 
using namespace std;

int main()
  {
   int dane[]={1,2,3,4,5,6,7,8};
   list<int> T(dane,dane+8);
   for(list<int>::iterator i=T.begin();i!=T.end();++++i) cout<<setw(2)<<*i;
   cin.get();
   return 0;
  } [/code]

Powyższe mogłeś sam wywnioskować z przedstawionej tabelki, z postów co zazwyczaj piszesz raczej wynika że masz głowę na karku, nie rozumiem poco tu udajesz kogoś kim nie jesteś.