Biểu thức + (+ k--) trong C


9

Tôi đã thấy câu hỏi này trong một bài kiểm tra trong đó chúng ta phải cho biết đầu ra của đoạn mã sau.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

Đầu ra là -1. Tôi không chắc chắn tại sao đây là câu trả lời, mặc dù.

Biểu thức +(+k--)có nghĩa gì trong C?


4
Nó có được định dạng như thế này không? Điều đó có nghĩa là ;-)
Peter - Tái lập lại

3
Gợi ý: không bao giờ viết mã BS như thế này trong cuộc sống thực.
Jabberwocky

5
Đây không phải là UB. Xem câu trả lời của tôi.
dbush

@ Peter-ReinstateMonica Có nó được định dạng như thế này.
Ankur Gautam

2
Bởi vì bản chất giả tạo của nó và những vòng xoắn kỳ quái - k=k++không được xác định, nhưng nó không được xác định bởi vì nó không bao giờ được thực hiện vì một điều kiện khó hiểu - Tôi đang bỏ phiếu để đóng như một bản sao của câu hỏi này .
Hội nghị thượng đỉnh Steve

Câu trả lời:


10

[Đố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ế kbạ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 đó:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. 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 kgiá 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ạ. 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 kgiá 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-1trở 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ì. +amang lại agiá 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à kgiá 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à kgiá 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ẽ:

  1. tiếp tục làm k--, để làm cho knhỏ hơn và nhỏ hơn, nhưng cũng
  2. 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 klà 0. Vì vậy, k--sẽ "trả lại" giá trị cũ 0, sau đó cập nhật klê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.


1
Các ví dụ có sẵn là điển hình trong bất kỳ lớp nền tảng nào. Làm thế nào người khác có thể viết một câu hỏi ngắn gọn về quyền ưu tiên của nhà điều hành trong một kỳ thi? Tôi dạy toán và nếu tôi có một chiếc niken cho mỗi lần học sinh hỏi "Khi nào tôi sẽ làm điều này trong cuộc sống thực ?" ...
Scott

4
@Scott có contrived, và sau đó có contrived . Giả sử bạn là một người hướng dẫn đào tạo lái xe. Giả sử bạn yêu cầu học sinh của mình lái xe qua giao thông trung tâm thành phố nặng nề trong khi đánh anh ta về đầu bằng gậy bóng chày. Có thể cho rằng, học sinh sẽ học được một kỹ năng có ích. Nhưng tôi gọi đây là lạm dụng. Và tôi đứng trước ý kiến ​​của tôi rằng mã trong câu hỏi này là lạm dụng, với giá trị sư phạm tiêu cực.
Hội nghị thượng đỉnh Steve

1
"Tôi đồng ý với ý kiến ​​của tôi" điều này là công bằng, nhưng từ khóa ở đây là "ý kiến". Câu trả lời StackOverflow có thể không phải là nơi tối ưu để bày tỏ ý kiến ​​của một người. :)
Scott

1
Đố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. Nó vẫn nói về cơ bản những điều tương tự, mặc dù.
Hội nghị thượng đỉnh Steve

11

Thoạt nhìn có vẻ như mã này gọi hành vi không xác định tuy nhiên đó không phải là trường hợp.

Trước tiên hãy định dạng mã chính xác:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

Vì vậy, bây giờ chúng ta có thể thấy rằng tuyên bố k=k++;là bên trong vòng lặp.

Bây giờ hãy theo dõi chương trình:

Khi điều kiện vòng lặp được đánh giá đầu tiên, kcó giá trị 0. Biểu thức k--có giá trị hiện tạik là 0 và kđược giảm xuống dưới dạng hiệu ứng phụ. Vì vậy, sau tuyên bố này, giá trị klà -1.

Dẫn đầu +trong biểu thức này không có ảnh hưởng đến giá trị, do đó được +k--đánh giá là 0 và tương tự +(+k--)đánh giá thành 0.

Sau đó, các !=nhà điều hành được đánh giá. Vì 0!=0là sai nên phần thân của vòng lặp không được nhập . Nếu cơ thể được nhập vào, bạn sẽ gọi hành vi không xác định vì k=k++cả đọc và viết kmà không có điểm thứ tự. Nhưng vòng lặp không được nhập, vì vậy không có UB.

Cuối cùng, giá trị của kđược in là -1.


1
Đó là một câu hỏi mở, mang tính tồn tại, triết học, không thể suy nghĩ liệu hành vi không xác định mà không được thực hiện không được xác định. Có phải nó không được xác định? Tôi không nghĩ vậy.
Hội nghị thượng đỉnh Steve

2
@SteveSummit Hoàn toàn không có gì tồn tại, triết lý, không thể tin được về bất cứ điều gì. if (x != NULL) *x = 42;Đây có phải là không xác định khi x == NULLnào? Dĩ nhiên là không. Hành vi không xác định không xảy ra trong các phần của mã không được thực thi. Các hành vi từ là một gợi ý. Mã không được thực thi không có hành vi, không xác định hoặc khác.
n. 'đại từ' m.

1
@SteveSummit Nếu hành vi không xác định không được thực thi sẽ làm mất hiệu lực toàn bộ chương trình, không có chương trình C nào có hành vi được xác định. Bởi vì các chương trình C luôn chỉ là một vòng lặp / nếu điều kiện không thực hiện UB. Lấy một phép lặp mảng đơn giản cho một ví dụ: Nó lặp đi lặp lại cho đến khi kiểm tra ràng buộc không thành công. Thực hiện vòng lặp tiếp theo sẽ là hành vi không xác định, nhưng vì điều đó không bao giờ được thực hiện, mọi thứ đều ổn.
cmaster - phục hồi monica

3
Tôi sẽ không tranh luận về điều đó, vì tôi không làm luật sư ngôn ngữ. Nhưng tôi cá là nếu bạn hỏi câu hỏi này và gắn thẻ nó là luật sư ngôn ngữ, bạn có thể có một cuộc tranh luận sôi nổi. Lưu ý rằng k=k++khác biệt về chất *x=42. Cái sau được xác định rõ nếu xlà một con trỏ hợp lệ, nhưng cái trước không được xác định cho dù thế nào đi chăng nữa. (Tôi thừa nhận bạn có thể đúng, nhưng một lần nữa, tôi sẽ không để tranh luận giáp nó, và tôi đang ngày càng quan tâm đến chúng tôi đã trolled masterfully.)
Steve Hội nghị thượng đỉnh

1
"Cái sau được xác định rõ nếu x là con trỏ hợp lệ, nhưng cái trước không được xác định bất kể là gì". Chỉ toàn bộ chương trình có thể có hành vi không xác định. Khái niệm này không áp dụng cho các đoạn mã được thực hiện trong sự cô lập.
n. 'đại từ' m.

3

Đây là phiên bản này cho thấy quyền ưu tiên của nhà điều hành:

+(+(k--))

Hai toán tử đơn nguyên +không làm gì cả, vì vậy biểu thức này hoàn toàn tương đương k--. Người viết ra điều này rất có thể đang cố gắng gây rối với tâm trí của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.