mk761203 napisał(a)
uważam że działanie jest ściśle określone
jeżeli mamy:
a = a++ + a;
to rozwijamy to następująco:
a = a + a;
a = a + 1;
drugi przypadek:
a = ++a + a;
rozwijamy do:
a = a + 1;
a = a + a;
stąd wyniki są deterministyczne
nie, nie są. porównaj proszę, efekty kompilacji tego kodu pod g++, msvc, i jeszcze inny jakis wymyśl, i prawie na pewno bedziesz mial rozne wyniki.
Standard C++ NIE DEFINIUJE KOLEJNOSCI wykonywania pod wyrazen. W javie i C# jest ona zdefiniowana i przywykles z niej korzystac (podswiadomie pewnie). Tutaj tak nie jest. Kolejnosc wykonywania podwyrazen to cos kompletnie innego niz priorytety operatorow!
wezmy np. a = 4 oraz wyrazenie: a + --a + a++
priorytety oraz semantyka operatorow i/lub rozsadek mowi, ze najpierw ma byc predecrement, potem suma, potem postincrement. powiedzmy ze jest to ok.
ale - skad ma pochodzic wartosc A dla poszczegolnych trzech wyrazen? w C++ nie jest powiedziane skad. ma pochodzic ze zmiennej A, z wartosci A niestarszej niz z poprzednio wykonanego wyrazenia, ale konkretnie ktore co ma sie wykonac najpierw NIE jest powiedizane. na jednym kompilatorze, moze zostac to wykonane tak:
a bylo na stosie = 4
mam wykonac a + --a + a++
biore a, zapamietuje w rejestrze
biore a, dekrementuje, zapamietuje w rejestrze, zapisuje nowe a na stos
biore a, zapamietuje w rejestrze, inkrementuje, zapisuje nowe a na stos
suma rejestr+rejestr+rejestr
wynik = 10
na innym kompilatorze moze byc to zrobione tak:
a bylo na stosie = 4
mam wykonac a + --a + a++
dekrementuje a, zapisuje nowe a na stos, zapamietuje w rejestrze
biore a, zapamietuje w rejestrze
biore a, zapamietuje w rejestrze, inkrementuje, zapisuje nowe a na stos
suma rejestr+rejestr+rejestr
wynik = 9
itd itp. I oba kompilatory maja racje, oba kompilatory SĄ POPRAWNE. Tak jak powiedzial świętowit, równie dobrze mogłoby tutaj wyjść 666 i też to by było poprawne zachowanie kompilatora.
W przeciwienstwie do Javy i C#, programista C++ ma obowiazek pilnowac i kontrolowac, aby wyrazenia czastkowe w danym wyrazeniu trzymaly sie zasady: albo wielokrotnie czytasz i nie zmieniasz, albo jednokrotnie czytasz i zmieniasz. NIE WOLNO zmieniac i czytac wielokrotnie tej samej wartosci.
tak jak a++ + a + --a czy slynne x+++++x sa z definicji niepoprawne, tak samo niepoprawne jest np.:
int a,b,c, *x = &a, &y = b;
c = a++ + --*x + --y; // wieloodczyt i wielozmiana z A (poprzez X oraz Y), kolejnosc podwyrazen nieokreslona
c = funkcja(a++, *x++, --y); // wieloodczyt i wielozmiana A.. kolejnosc wykonywania podwyrazen nieokreslona (tu: podwyrazenie=wyliczenie wartosci argumentu)
mam nadzieje, że teraz rozumiesz, czemu należy potrzykroć sprawdzać na czym się operuje i jeżeli to możliwe, starać sie uzywać maksymlanie jedno-dwa ++/-- w jednej linii. niestety automatyczna statyczna weryfikacja kodu jest jeszcze za malo rozwinieta, aby kompilator mogl zglaszac wszystkie w/w wpadki jaki przynajmniej warningi.. niektore wydaje mi sie ze przy prostych wyrazeniach zglaszaja:) ale - jedynie wydaje mi sie.