[Đối với hồ sơ, tôi đã chỉnh sửa câu trả lời này khá đáng kể vì nó đã được chấp nhận và bỏ phiếu. Về cơ bản, nó vẫn nói những điều tương tự.]
Mã này là sâu sắc, có lẽ cố tình, khó hiểu. Nó chứa một thể hiện bị đảo ngược của hành vi không xác định đáng sợ . Về cơ bản, không thể xác định liệu người xây dựng câu hỏi này đã rất, rất thông minh hay rất, rất ngu ngốc. Và "bài học" mã này có thể có ý định dạy hoặc đố bạn về - cụ thể là toán tử unary plus không làm được gì nhiều - chắc chắn không phải là một điều đủ quan trọng để xứng đáng với loại sai lầm lật đổ này.
Có hai khía cạnh khó hiểu của mã, điều kiện lạ:
while(+(+k--)!=0)
và tuyên bố mất trí mà nó kiểm soát:
k=k++;
Tôi sẽ trình bày phần thứ hai trước.
Nếu bạn có một biến như thế k
bạn muốn tăng thêm 1, C cung cấp cho bạn không phải một, không phải hai, không phải ba, mà là bốn cách khác nhau để làm điều đó:
k = k + 1
k += 1
++k
k++
Bất chấp tiền thưởng này (hoặc có lẽ vì nó), một số lập trình viên bị lẫn lộn và ho ra những mâu thuẫn như
k = k++;
Nếu bạn không thể hiểu điều này cần phải làm gì, đừng lo lắng: không ai có thể. Biểu thức này chứa hai lần thử khác nhau để thay đổi k
giá trị ( k =
phần và k++
phần) và bởi vì không có quy tắc nào trong C để nói rằng sửa đổi nào đã cố gắng "thắng", một biểu thức như thế này không được xác định chính thức , có nghĩa là không chỉ nó không có nghĩa rõ ràng, nhưng toàn bộ chương trình chứa nó là nghi ngờ.
Bây giờ, nếu bạn xem xét rất kỹ, bạn sẽ thấy rằng trong chương trình cụ thể này, dòng k = k++
không thực sự được thực thi, bởi vì (như chúng ta sắp thấy) điều kiện kiểm soát ban đầu là sai, do đó vòng lặp chạy 0 lần . Vì vậy, chương trình cụ thể này có thể không thực sự không được xác định - nhưng nó vẫn gây nhầm lẫn về mặt bệnh lý.
Xem thêm những câu trả lời SO chuẩn cho tất cả các câu hỏi liên quan đến Hành vi không xác định của loại này.
Nhưng bạn đã không hỏi về k=k++
phần này. Bạn hỏi về phần khó hiểu đầu tiên, +(+k--)!=0
điều kiện. Điều này có vẻ lạ, bởi vì nó là lạ. Không ai sẽ từng viết mã như vậy trong một chương trình thực sự. Vì vậy, không có lý do để tìm hiểu làm thế nào để hiểu nó. . phía sai của dòng đó.)
Dù sao, hãy kiểm tra +(+k--)!=0
. (Và sau khi làm như vậy, chúng ta hãy quên tất cả về nó.) Bất kỳ biểu thức nào như thế này phải được hiểu từ trong ra ngoài. Tôi đoán bạn biết những gì
k--
làm. Nó nhận k
giá trị hiện tại và "trả" nó cho phần còn lại của biểu thức và nó ít nhiều đồng thời giảm k
, nghĩa là, nó lưu trữ số lượng k-1
trở lại k
.
Nhưng sau đó thì +
làm gì? Đây là cộng đơn , không phải cộng nhị phân. Nó giống như là một điểm trừ đơn nhất. Bạn biết rằng phép trừ nhị phân không trừ: biểu thức
a - b
trừ b từ a. Và bạn biết rằng dấu trừ đơn phương phủ nhận mọi thứ: biểu thức
-a
cung cấp cho bạn tiêu cực của a. Những gì unary +
làm là ... về cơ bản không có gì. +a
mang lại a
giá trị cho bạn , sau khi thay đổi giá trị dương thành giá trị dương và âm thành âm. Vì vậy, biểu thức
+k--
mang lại cho bạn bất cứ điều gì k--
đã cho bạn, đó là k
giá trị cũ.
Nhưng chúng ta chưa xong, vì chúng ta có
+(+k--)
Điều này chỉ cần bất cứ điều gì +k--
đã cho bạn, và áp dụng unary +
cho nó một lần nữa. Vì vậy, nó mang lại cho bạn bất cứ thứ gì +k--
mang lại cho bạn, đó là bất cứ thứ gì k--
mang lại cho bạn, đó là k
giá trị cũ.
Vì vậy, cuối cùng, điều kiện
while(+(+k--)!=0)
thực hiện chính xác điều tương tự như điều kiện bình thường hơn nhiều
while(k-- != 0)
sẽ làm (Nó cũng làm điều tương tự như điều kiện trông phức tạp hơn nữa while(+(+(+(+k--)))!=0)
sẽ xảy ra. Và những dấu ngoặc đơn đó không thực sự cần thiết; nó cũng làm điều tương tự như while(+ + + +k--!=0)
đã làm.)
Thậm chí tìm ra điều kiện "bình thường"
while(k-- != 0)
không phải là loại khó khăn Có hai điều đang diễn ra trong vòng lặp này: Khi vòng lặp chạy tiềm năng nhiều lần, chúng tôi sẽ:
- tiếp tục làm
k--
, để làm cho k
nhỏ hơn và nhỏ hơn, nhưng cũng
- tiếp tục làm cơ thể của vòng lặp, bất cứ điều gì làm.
Nhưng chúng tôi thực hiện k--
một phần ngay lập tức, trước khi (hoặc trong quá trình) quyết định có nên thực hiện một chuyến đi khác qua vòng lặp hay không. Và hãy nhớ rằng k--
"trả về" giá trị cũ của k
, trước khi giảm giá trị đó. Trong chương trình này, giá trị ban đầu k
là 0. Vì vậy, k--
sẽ "trả lại" giá trị cũ 0, sau đó cập nhật k
lên -1. Nhưng sau đó, phần còn lại của điều kiện là != 0
- nhưng như chúng ta vừa thấy, lần đầu tiên chúng tôi kiểm tra điều kiện, chúng tôi đã nhận được 0. Vì vậy, chúng tôi sẽ không thực hiện bất kỳ chuyến đi nào qua vòng lặp, vì vậy chúng tôi sẽ không cố gắng thực hiện tuyên bố có vấn đề k=k++
ở tất cả.
Nói cách khác, trong vòng lặp cụ thể này, mặc dù tôi đã nói rằng "có hai thứ đang diễn ra", nhưng hóa ra điều 1 xảy ra một lần, nhưng điều 2 xảy ra 0 lần.
Ở mức độ nào, tôi hy vọng giờ đây nó đã rõ ràng đầy đủ lý do tại sao lý do nghèo nàn này cho một chương trình kết thúc việc in -1 là giá trị cuối cùng của k
. Thông thường, tôi không thích trả lời các câu hỏi đố như thế này - cảm giác như gian lận - nhưng trong trường hợp này, vì tôi rất không đồng ý với toàn bộ quan điểm của bài tập, tôi không bận tâm.