2013-07-04 17 views
12

Rzekomo „mądry” (ale w rzeczywistości nieskuteczne) sposób zamiana dwóch zmiennych całkowitych, zamiast stosowania tymczasowego magazynowania, często wiąże się z tej linii: Czy w wyrażeniu a^= b^= a^= b są punkty sekwencyjne, czy jest niezdefiniowane?

int a = 10; 
int b = 42; 

a ^= b ^= a ^= b; /*Here*/ 

printf("a=%d, b=%d\n", a, b); 

Ale zastanawiam się, jak złożone operatory przypisania są ^= nie punkty kolejności, prawda? Czy to oznacza, że ​​jest to nieokreślone zachowanie?

+2

Jeśli piszesz kod, który jest trudny do określenia, co się dzieje, zadaj sobie pytanie, czy istnieje bardziej prosty sposób, który może zrozumieć przyszły programista? –

+1

Zauważ, że jeśli widziałeś to w C++, C++ ma różne reguły dla operatorów przypisania, które zezwalają na pewne konstrukcje (nie jestem tego pewien), które są niezdefiniowane w C. – hvd

+3

możliwy duplikat [Sequence Point - Xor Zamień na Array otrzymasz zły wynik] (http://stackoverflow.com/questions/9958514/sequence-point-xor-swap-on-array-get-wrong-result) –

Odpowiedz

17
a ^= b ^= a ^= b; /*Here*/ 

Jest to niezdefiniowane zachowanie.

Użytkownik modyfikuje obiekt (a) więcej niż jeden raz między dwoma punktami sekwencji.

(C99 6.5p2) „między poprzednim i następnym punkcie sekwencji przedmiot umieszcza się jego wartość przechowywana zmodyfikowany w więcej niż raz w ocenie wyrażenia.

proste zadania jak związek zadania nie wprowadzają punkt sekwencji. Tu jest punkt sekwencja przed wyrażeniem rachunku ekspresja i po zestawieniu ekspresji.

punkty sekwencji są wymienione w załączniku C (informacyjny) z C99 i C11 standard.

13

^= nie są punktami sekwencji, są

nie.

Czy to oznacza niezdefiniowane zachowanie?

Tak jest. Nie używaj tej "sprytnej" techniki.

+0

Dzięki za potwierdzenie. Nie martw się, nigdy nie zamierzałem go używać. – Medinoc

7

Brak punktów sekwencji w tym wyrażeniu, więc powoduje niezdefiniowane zachowanie.

Można go naprawić trywialnie i zachowują większość zwięzłość za pomocą operatora przecinek, który ma wprowadzenia punktów kolejności:

a ^= b, b ^= a, a ^= b; 
+1

Równie dobrze możesz umieścić go na trzech różnych liniach w tym miejscu. Sprawi, że ładne pudełko, jeśli troszczysz się o te rzeczy .. – Thomas

5

Kolejność oceny ^= operatorów jest dobrze zdefiniowana. To, co nie jest dobrze zdefiniowane, to kolejność modyfikacji a i b.

a ^= b ^= a ^= b; 

jest równoważna

a ^= (b ^= (a ^= b)); 

Operator nie może być oceniane przed jej argumenty są oceniane, więc na pewno będzie wykonać a ^= b pierwszy.

Przyczyną tego zachowania jest niezdefiniowanie, ponieważ w celu zapewnienia kompilatorowi większej elastyczności w dokonywaniu optymalizacji dozwolona jest modyfikacja wartości zmiennych w dowolnej kolejności.To może wybrać to zrobić:

int a1 = a^b; 
int b1 = b^a1; 
int a2 = a^b1; 
a = a1; 
a = a2; 
b = b1; 

lub to:

int a1 = a^b; 
int b1 = b^a1; 
a = a1; 
int a2 = a^b1; 
a = a2; 
b = b1; 

lub nawet poniżej:

int a1 = a^b; 
int b1 = b^a1; 
int a2 = a^b1; 
a = a2; 
a = a1; 
b = b1; 

Jeśli kompilator mógł wybrać tylko jedną z tych trzech sposobów, aby robić rzeczy, byłoby to po prostu "nieokreślone" zachowanie. Jednak standard idzie dalej i sprawia, że ​​jest to zachowanie "niezdefiniowane", które zasadniczo pozwala kompilatorowi zakładać, że nawet nie może się zdarzyć.

+0

Pierwsze wyjaśnienie jest ostateczne, ale nie mogłem zrozumieć przyczyny: * "wstępnie zmodyfikowane lub post-zmodyfikowane wartości" * –

+1

To jest mylące. Nic nie stoi na przeszkodzie, aby efekt uboczny wyrażenia się wydarzył po zakończeniu oceny. Wynikiem 'a^= b' jest' a^b', a jako efekt uboczny, 'a' jest ustawiony na ten wynik. * Gdy * 'a' jest ustawione na ten wynik, nie jest określone. W szczególności nie ma niczego, co by wymagało ukończenia * przed * zewnętrznym początkiem 'a^= ...'. – hvd

+0

@hvd: Zgadzam się. Próbowałem go zmienić, aby było jaśniejsze. –

Powiązane problemy