2013-07-04 43 views
12

Cách bị cáo buộc "thông minh" (nhưng thực sự không hiệu quả) của trao đổi hai biến số nguyên, thay vì sử dụng lưu trữ tạm thời, thường liên quan đến dòng này: Có các điểm trình tự trong biểu thức a^= b^= a^= b hay không xác định?

int a = 10; 
int b = 42; 

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

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

Nhưng tôi tự hỏi, hợp chất toán tử gán như ^= là không phải là điểm chuỗi, đúng không? Điều này có nghĩa là nó thực sự là hành vi không xác định?

+2

Nếu bạn viết mã khó mà nói được những gì đang xảy ra, hãy tự hỏi liệu có cách nào đơn giản hơn mà nhà phát triển tương lai có thể hiểu không? –

+1

Lưu ý rằng nếu bạn đã thấy điều này trong mã C++, C++ có các quy tắc khác nhau cho toán tử gán cho phép cấu trúc nhất định (tôi không chắc chắn về cấu trúc này) không xác định trong C. – hvd

+3

có thể trùng lặp của [Sequence Point - Xor Hoán đổi trên Array nhận được kết quả sai] (http://stackoverflow.com/questions/9958514/sequence-point-xor-swap-on-array-get-wrong-result) –

Trả lời

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

Hành vi không xác định.

Bạn đang sửa đổi một đối tượng (a) nhiều hơn một lần giữa hai điểm chuỗi.

(C99, 6.5p2) "Giữa các điểm trình tự trước và bên cạnh một đối tượng có trách nhiệm đã được lưu trữ giá trị của nó biến đổi cùng lúc nhiều nhất bởi việc đánh giá một biểu thức.

bài tập đơn giản cũng như phức hợp bài tập không giới thiệu một điểm theo thứ tự. ở đây có một điểm chuỗi trước khi biểu thức tuyên bố thể hiện và sau khi báo cáo kết quả biểu hiện.

điểm trình tự được liệt kê trong Phụ lục C (Tham khảo) của c99 và C11 Standard.

13

^= không điểm theo thứ tự, là họ

Họ không phải.

Điều này có nghĩa là hành vi thực sự không xác định?

Vâng. Không sử dụng kỹ thuật "thông minh" này.

+0

Cảm ơn bạn đã xác nhận. Đừng lo lắng, tôi không bao giờ có ý định sử dụng nó ngay từ đầu. – Medinoc

7

Không có điểm chuỗi trong biểu thức đó, do đó, nó tạo ra hành vi không xác định.

Bạn có thể sửa chữa nó trivially và giữ lại hầu hết các tính cô đọng bằng cách sử dụng toán tử dấu phẩy, mà không giới thiệu điểm trình tự:

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

Bạn cũng có thể đặt nó trên ba dòng khác nhau tại thời điểm này. Nó sẽ tạo ra một hộp đẹp, nếu bạn quan tâm đến những điều đó .. – Thomas

5

Thứ tự của các đánh giá của các nhà khai thác ^= cũng được xác định. Những gì không được xác định rõ ràng là thứ tự trong đó ab được sửa đổi.

a ^= b ^= a ^= b; 

tương đương với

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

Một nhà điều hành không thể được đánh giá trước đối số của nó được đánh giá, vì vậy nó chắc chắn sẽ thực hiện a ^= b đầu tiên.

Lý do để có hành vi không xác định này là, để cho trình biên dịch linh hoạt hơn trong việc tối ưu hóa, nó được phép sửa đổi các giá trị biến theo bất kỳ thứ tự nào nó chọn.Nó có thể chọn để làm điều này:

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

hay này:

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

hoặc ngay cả điều này:

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

Nếu trình biên dịch chỉ có thể chọn một trong ba cách để làm điều gì đó, điều này sẽ chỉ là hành vi "không xác định". Tuy nhiên, tiêu chuẩn đi xa hơn và làm cho hành vi này là "không xác định", về cơ bản cho phép trình biên dịch giả định rằng nó thậm chí không thể xảy ra.

+0

giải thích đầu tiên là cuối cùng, nhưng tôi không thể hiểu lý do: * 'tiền sửa đổi hoặc giá trị sửa đổi sau * –

+1

Điều này gây hiểu nhầm. Không có gì ngăn cản tác dụng phụ của một biểu hiện xảy ra sau khi việc đánh giá đã hoàn thành. Kết quả của 'a^= b' là' a^b', và như là một hiệu ứng phụ, 'a' được đặt thành kết quả đó. * Khi * 'a' được đặt thành kết quả đó là không xác định. Đặc biệt, không có gì yêu cầu nó phải hoàn thành * trước * bắt đầu 'a^= ...' bên ngoài. – hvd

+0

@hvd: Tôi đồng ý. Tôi đã cố gắng để viết lại nó để làm cho nó rõ ràng hơn. –

Các vấn đề liên quan