Tại sao câu lệnh if này kết hợp phép gán và kiểm tra đẳng thức trả về đúng?


216

Tôi đã nghĩ về một số sai lầm của người mới bắt đầu và tôi đã kết thúc với một trong những if tuyên bố. Tôi đã mở rộng một chút mã này:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

Tôi đã thấy rằng iftuyên bố trả về đúng và nó coutinhư vậy 1. Nếu iđược gán 1trong câu lệnh if, tại sao i == 0lại trả về true?


83
Các bạn, đây không phải là một câu hỏi chính tả. OP muốn biết lý do tại sao câu lệnh if được nhập với mã này vì iđược đặt thành 1.
NathanOliver

24
Hoặc nó chỉ định kết quả của 1 && i == 0?
JVApen

4
Gợi ý cho người mới bắt đầu: Họ không nên sử dụng cấu trúc ngôn ngữ "nâng cao" như vậy. Chỉ cần gán biến riêng. Điều đó cũng sẽ tránh các vấn đề có thể xảy ra với điểm trình tự. Loại mã này trong mã thực tế thường sẽ trông cũng xấu.
dùng202729

1
điều này chắc chắn sẽ kết thúc với một câu hỏi phỏng vấn
RAZ_Muh_Taz

Câu trả lời:


391

Điều này phải làm với ưu tiên nhà điều hành .

if (i = 1 && i == 0)

không phải

if ((i = 1) && (i == 0))

bởi vì cả hai &&==có độ ưu tiên cao hơn =. Những gì nó thực sự làm việc là

if (i = (1 && (i == 0)))

mà gán kết quả của 1 && (i == 0)để i. Vì vậy, nếu ibắt đầu lúc 0đó i == 0true, 1 && truethì true(hoặc 1), và sau đó iđược đặt thành 1. Sau đó 1là đúng, bạn nhập khối if và in giá trị bạn đã gán i.


2
@ JörgWMittag Điều đó thật tuyệt. Tôi thích điều đó buộc bạn phải sử dụng dấu ngoặc đơn.
NathanOliver

Về cơ bản, i = !i; if (i)được viết đúng
Cacahuete Frito

@NathanOliver: Pháo đài là một ngôn ngữ khá tuyệt vời và có rất nhiều điều đúng. (Nhà thiết kế chính là Guy L. Steele, vì vậy không có gì ngạc nhiên.) Thật không may, nó đã không được chọn cho vòng tài trợ DARPA cuối cùng, và sau đó đã bị Oracle đánh bại.
Jörg W Mittag

6
Đương nhiên, yêu cầu một chút cảnh báo từ trình biên dịch sẽ phát hiện ra lỗi đó,
Ded repeatator

4
Bất kỳ ngôn ngữ nào không giả sử ints và booleans là tương đương cũng sẽ chọn ngôn ngữ này.
rghome

16

Giả sử mã của bạn thực sự trông như thế này:

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

Thì đây:

if (i = 1 && i == 0) {

đánh giá như

 if (i = (1 && i == 0)) {

và như vậy iđược đặt thành 1.


39
Là mã bổ sung thực sự cần thiết? Có vẻ như khá rõ ràng rằng đây sẽ là trường hợp vì nó sẽ không chạy khác.
Người mẫu

13
Không chỉ mã bổ sung không cần thiết. Câu trả lời không giải thích rõ ràng về quyền ưu tiên của nhà điều hành.
Francisco Zarabozo

34
Vì chúng ta đang ở trên chuyến tàu nitpick ... tôi hiểu using namespace stdrồi!
Mateen Ulhaq

8
Có thêm mã - nhưng nó vẫn không phải là mã không chính xác. Và câu trả lời vẫn đúng. Tất nhiên, nó không giải thích quyền ưu tiên của nhà điều hành. Nhưng ai đó có thể đề nghị thêm nó, thay vì bỏ phiếu hoàn toàn!
B Charles H

14
Wow, -4 là khó khăn, xem xét câu trả lời này chính xác, mặc dù có thể không tối ưu. Nó không mở rộng về quyền ưu tiên của nhà điều hành nhiều như câu trả lời khác, nhưng nó nói vừa đủ về nó trong ngữ cảnh của mã để bất kỳ ai nghĩ rằng =đến trước &&đều có thể thấy vấn đề. Ngoài ra, vâng, việc mở rộng là không liên quan, nhưng tôi không nghĩ nó quan trọng đến thế. Tôi không thể tin những khác biệt nhỏ như vậy khiến mọi người bỏ phiếu từ 151 đến -4.
JoL

-4

Nó phải làm với việc phân tích cú pháp từ phải sang trái. Ví dụ: y = x + 5.
Tất cả các biểu thức phụ được coi trọng. Hai biểu thức có tầm quan trọng như nhau được đánh giá từ phải sang trái ,. Phía biểu thức && được thực hiện trước, tiếp theo là LHS.

Có nghĩa với tôi.


1
Associativity ("quy tắc từ phải sang trái") không liên quan gì đến điều này. Đó là về quyền ưu tiên ("tầm quan trọng") và các toán tử được sử dụng không có quyền ưu tiên như nhau.
Các cuộc đua nhẹ nhàng trong quỹ đạo

-4

Câu trả lời thực tế là:

  1. Trình biên dịch ưu tiên "i == 0", đánh giá là đúng.
  2. Sau đó, nó sẽ đánh giá i = 1 là TRUE hoặc FALSE và vì các toán tử gán được biên dịch không bao giờ thất bại (nếu không chúng sẽ không biên dịch), nó cũng đánh giá là đúng.
  3. Vì cả hai câu lệnh đều đánh giá là đúng và TRUE && TRUE đánh giá là TRUE, nên câu lệnh if sẽ đánh giá thành TRUE.

Để chứng minh, chỉ cần nhìn vào đầu ra asm của trình biên dịch của bạn cho mã bạn đã nhập (tất cả các nhận xét là của riêng tôi):

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

Đầu ra asm ở trên là từ CLANG, nhưng tất cả các trình biên dịch khác mà tôi đã xem đều cho đầu ra tương tự. Điều này đúng với tất cả các trình biên dịch trên trang web đó, cho dù chúng là trình biên dịch C hay C ++ thuần túy, tất cả đều không có bất kỳ trình duyệt nào để thay đổi chế độ của trình biên dịch (theo mặc định là C ++ cho trình biên dịch C ++)

Lưu ý rằng trình biên dịch của bạn không thực sự đặt i = 1, nhưng i = TRUE (có nghĩa là bất kỳ giá trị nguyên 32 bit nào không bằng 0). Đó là bởi vì toán tử && chỉ đánh giá xem một câu lệnh là TRUE hay FALSE, sau đó đặt kết quả theo kết quả đó. Để chứng minh, hãy thử thay đổi i = 1 thành i = 2 và bạn có thể tự quan sát rằng không có gì thay đổi. Xem cho chính mình bằng bất kỳ trình biên dịch trực tuyến nào tại Compiler Explorer


1
1) Các tài liệu liên kết đến ưu tiên toán tử C, khi câu hỏi này được gắn thẻ với C ++. Hai ngôn ngữ khác nhau. 2a) i = 1là toán tử gán [không phải tương đương]; 2b) Tôi có thể đảm bảo với bạn rằng if (i = 0)sẽ đánh giá tình trạng sai trong cả C và C ++, do đó, liệu nó có đánh giá đúng hay không "nó không bao giờ thất bại" là hơi sai.
TrebledJ

1
and cl, 1 ; = operator always TRUE<< sửa tôi nếu tôi sai, nhưng tôi thấy không có bài tập nào ở đây. Nó đại diện cho 1 &&một phần của biểu thức. Vì vậy, câu trả lời này về cơ bản đánh giá false.
syck

"Các tài liệu liên kết đến quyền ưu tiên của toán tử C, khi câu hỏi này được gắn thẻ C ++. Hai ngôn ngữ khác nhau" - và khi bạn so sánh ưu tiên toán tử C với C ++, sự khác biệt giữa hai ngôn ngữ này là gì? Họ có cùng mức độ ưu tiên đối với chủ đề này, điều này không gây ngạc nhiên, vì C ++ là một dẫn xuất trực tiếp của C (hay nói cách khác là C là một tập hợp con của ngôn ngữ C ++, nên dĩ nhiên họ sẽ có rất nhiều điểm chung, bao gồm cả quyền ưu tiên). Tôi sẽ sửa bài viết của mình bằng mọi cách, trong trường hợp khó hiểu.
ar18

"Sửa lỗi cho tôi nếu tôi sai, nhưng tôi thấy không có sự phân công nào ở đây" - Sau đó hãy để tôi sửa lỗi cho bạn! 1 là một giá trị ngay lập tức và không phải là kết quả của bất kỳ thử nghiệm hoặc tính toán nào. Đó là giá trị được gọi là "TRUE". Thử nghiệm duy nhất diễn ra là cho câu lệnh i == 0, tức là - "cmp dword ptr [rbp - 8], 0". Bạn sẽ chỉ đúng nếu nó nói "Movzx edx, 1". Theo TẤT CẢ các bài viết trước của tôi, cần có hai so sánh, nhưng trong thực tế chỉ có một, và đầu ra asm của MỌI trình biên dịch chính chứng minh rằng các bài đăng đó hoàn toàn không chính xác.
ar18

2
Cũng như nhận được quyền ưu tiên sai (xem câu trả lời của NathanOliver để phân tích chính xác), bạn đưa ra tuyên bố sai rằng toán tử gán luôn luôn đánh giá TRUE. Hãy thử if ( i = 0 ) { print something }. Ngoài ra câu trả lời của bạn mâu thuẫn với chính nó; khi bắt đầu, bạn nói rằng i=1được đánh giá trước khi &&được áp dụng, và cuối cùng bạn nói rằng nó iđược đặt thành kết quả của &&toán tử.
MM
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.