2017-02-03 32 views
21

Wiem, że i=i++; jest niezdefiniowanym zachowaniem, ponieważ i jest zmieniany dwa razy przed punktem sekwencji ;.Czy następujące niezdefiniowane zachowanie? i = func (i)

Ale ja nie wiem, czy kompilator gwarantuje sprawę jak poniżej nie jest niezdefiniowane zachowanie:

int func(int &i) 
{ 
    i++; 
    return i; 
} 

int i = 1; 
i = func(i); 
+0

co informuje kompilator? – tristan

+24

@tristan zazwyczaj kompilator nic nie mówi o UB. – user2079303

+0

gcc ma opcję "-Wsequence-point" – tristan

Odpowiedz

44

Po pierwsze, nowoczesny C++ został przełączony z (niedostatecznej) koncepcji starej „punktów sekwencji” do nowa koncepcja "sekwencjonowania" (tj. "sekwencjonowanie przed", "sekwencjonowanie po"). Chociaż i = i++ jest wciąż niezdefiniowany, to teraz jest on idealnie zdefiniowany. Reguły sekwencjonowania wielu operatorów powracających do luminancji zostały przerobione.

Po drugie, twoja wersja jest bezpieczna pod starą specyfikacją, a także pod nową. Modyfikacja i wewnątrz funkcji jest bezpiecznie "odizolowana" od przypisania do i na zewnątrz. W klasycznych punktach specyfikacji punkty na początku i na końcu funkcji bezpiecznie oddzieliły modyfikacje (i odczyty) od siebie o i. Nowe zasady sekwencjonowania zachowują ten sam poziom ochrony.

Przykładem ilustrującym ochrony zapewnianej przez wywołanie funkcji może wyglądać następująco

int inc(int &i) { return i++; } 
... 
int i = 1; 

int r1 = i++ * i++ * i++;   
// Undefined behavior because of multiple unsequenced side effects 
// applied to the same variable 

int r2 = inc(i) * inc(i) + inc(i); 
// No UB, but order of evaluation is unspecified. Since the result 
// depends on the order of evaluation, it is unspecified 

int r3 = inc(i) + inc(i) + inc(i); 
// Perfectly defined result. Order of evaluation is still unspecified, 
// but the result does not depend on it 
+0

"Modyfikacja i wewnątrz funkcji jest bezpiecznie" odizolowana "od przypisania do i na zewnątrz." Co jeśli kompilator włącza funkcję? A może potencjał UB oznacza, że ​​kompilator nie będzie uwzględniał tego konkretnego przypadku? – JAB

+4

@JAB: Inlining w żaden sposób nie zmienia semantyki sekwencyjnej (lub jakiejkolwiek innej semantyki) wywołania funkcji. Kompilator może dowolnie wstawiać w dowolnym momencie, ale wynikowy kod musi w pełni zachować oryginalną semantykę abstrakcyjną. To znaczy. zachowanie wywołanego połączenia musi być takie samo, jak zachowanie nieliniowanego połączenia. W tym przypadku nie ma nic, co uniemożliwiłoby kompilatorowi wpisywanie. Ale kompilator musi pamiętać, że wersja z zaznaczoną wersją w pełni zachowa zachowanie nieliniowego. – AnT

+0

Cóż, to dobrze, biorąc pod uwagę, że korzystałem z tego rodzaju zachowań w przeszłości. – JAB