2013-08-25 18 views
14

Mam następujący kod.Jaki jest powód tego wyjścia?

int x=80; 
    int &y=x; 
    x++; 
    cout<<x<<" "<<--y; 

Wynik wyjściowy to 80 80. I nie rozumiem w jaki sposób. Myślałem, że wynik x będzie 81, chociaż nie wiem nic o y. W jaki sposób wpływa na zmienną referencyjną operator dekrementacji? Czy ktoś może mi wyjaśnić?

+1

Prawdopodobnie to pytanie powinno ci to wyjaśnić: Jakie są różnice między zmienną wskaźnika a zmienną referencyjną w C++? (http://stackoverflow.com/questions/57483/what-are-the-differences-between-pointer-variable-and-reference-variable-in-c) – yasouser

+0

To jest podobne (do http://stackoverflow.com/questions/18426473/pre-increment-not-working-as-i-expected/18426505 # 18426505). Brakuje punktu sekwencji. –

Odpowiedz

6

Jedną z nieprzyjemnych rzeczy na temat operatorów przekierowań << jest to, że intuicyjnie przekazują one indea "obliczeniowych obliczeń", których rzeczywiście nie ma.

Kiedy piszesz

std::cout << f() << g() << std::endl; 

wyjście pokaże pierwszy wynik f() a następnie wynik g(), ale rzeczywiste wywołanie g() może się zdarzyć przed wywołaniem f().

Jest nawet gorzej niż to ... nie jest tak, że sekwencja nie jest przewidywalna, ale że sama koncepcja sekwencji jest nieprawidłowa. W

std::cout << f(g()) << h(i()) << std::endl; 

to na przykład prawne, że pierwsza funkcja nazywany jest g(), a następnie i(), a następnie h() i wreszcie przez f(). Nie ma nawet gwarancji, że zamówienie będzie takie samo dla wszystkich wywołań (nie dlatego, że twórcy kompilatorów lubią się z ciebie śmiać, ale dlatego, że kod może być wstawiony i kompilator może zdecydować o innej kolejności, jeśli funkcja zawierająca jest inkrementowana w innym kontekście).

Jedyne operatory C++, które gwarantują sekwencję w celu oceny są:

  1. &&: najpierw ocenia lewa strona i tylko wtedy, gdy wynik jest „true” ocenia prawej stronie
  2. ||: najpierw ocenia lewej strony i tylko wtedy, gdy wynik jest „false” ocenia prawej stronie
  3. ?:: ocenia pierwszy warunek, a potem dopiero drugi lub trzeci operand
  4. ,: operator przecinka ... ocenia lewą stronę, odrzuca wartość, a następnie ocenia i zwraca prawą stronę. UWAGA: przecinki między parametrami funkcji NIE są operacjami przecinkowymi i nie jest narzucana kolejność oceny.

Ponadto ten guarate jest ważny tylko dla predefiniowanych operatorów. Jeśli przeładujesz &&, || lub , w swojej klasie, są to zwykli operatorzy bez żadnych specjalnych ograniczeń dotyczących kolejności oceniania.

Dowolny inny operator nie nakłada żadnych ograniczeń na zlecenie oceny, a dotyczy to również użycia w rodzaju sztuczek, które można tak myśleć.

19

Wyrażenie jest oceniane jako:

((cout << x) << " ") << --y; 

Nie ma sensu sekwencja (lub zamawiania) pomiędzy oceną po lewej stronie i po prawej stronie wyrażenia, kompilator może kodem wyjścia który ocenia --y jako pierwszy krok.

Ponieważ y jest odniesieniem do x, jest to w rzeczywistości niezdefiniowane zachowanie, ponieważ zarówno czytasz, jak i piszesz do x w tym samym wyrażeniu bez pośrednich punktów sekwencji.

+0

Proszę sprawdzić edytowany kod Z pewnością ma to coś wspólnego z arytmetyką wskaźnika. Kiedy mówimy, że y ma adres x tj. 80 i spróbuj zmniejszyć i y, to również nie zmniejsza się w moim systemie. –

+4

@SanyamGoel: to niezdefiniowane zachowanie. Różne kompilatory mogą (i będą) wypuszczać różne rzeczy. Kolejność oceny LHS i RHS jest zdefiniowana przez implementację, plus odczyt i zapis do 'x' bez punktów sekwencji => niezdefiniowane zachowanie (to znaczy nie jest właściwe C++, może zrobić wszystko). – Mat

+0

Sugerowana edycja: * "Zobacz [ten wątek] (http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points), aby uzyskać szczegółowe wyjaśnienie."* – jrok

1

To niezdefiniowane zachowanie, C/C++ normy nie określają sposób argumentu są wypychane na stosie, a zwykle argumentem są wypychane w odwrotnej kolejności, na przykład tutaj:

func(1, 2) 

będą oceniane coś jak:

push 2 
push 1 
call func 

więc w Twoim przypadku, --y jest oceniany i pchnął przed x robi. Oczywiste jest, w tym przykładzie:

#include <iostream> 

int a() { std::cout << "a" << std::endl ; return 1; } 
int b() { std::cout << "b" << std::endl ; return 2; } 

int main(void) { 

    std::cout << a() << " " << b() << std::endl; 

    return 0; 
} 

od pierwszego spojrzenia, należy wydrukować:

a 
b 
1 2 

ale drukuje: Przyrosty

b 
a 
1 2 
0
x++ 

x i produkuje jako wyrażenie wynik pierwotna wartość x.

W szczególności, dla x ++ nie ma żadnego porządkowania czasowego implikowanego dla przyrostu i produkcji pierwotnej wartości x. Kompilator może emitować kod maszynowy, który daje oryginalną wartość x, np. może być obecny w jakimś rejestrze, który opóźnia inkrement do końca wyrażenia (następny punkt sekwencji). Chociaż wydaje się, że x++ zwiększa się o x do , nie robi tego aż do druku. Zgodnie z --y, pobiera on wartość inkrementowaną (81) i zmniejsza ją przed wydrukowaniem, ponieważ jest to operator o numerze z prefiksem.

Powiązane problemy