ทำไมคำสั่ง if-if ที่รวมการมอบหมายและการตรวจสอบความเท่าเทียมกลับมาจริงหรือไม่


216

ฉันได้รับความคิดของความผิดพลาดเริ่มต้นบางอย่างและฉันจบลงด้วยหนึ่งในifคำสั่ง ฉันขยายโค้ดเล็กน้อยถึงสิ่งนี้:

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

ฉันได้เห็นว่าifผลตอบแทนที่คำสั่งจริงและมันcoutก็เป็นi 1หากiได้รับมอบหมาย1ในงบถ้าทำไมไม่i == 0กลับมาtrue?


83
พวกนี้ไม่ใช่คำถามที่พิมพ์ผิด สหกรณ์ต้องการที่จะรู้ว่าทำไมถ้ามีคำสั่งถูกป้อนด้วยรหัสนี้ตั้งแต่มีการตั้งค่าi 1
NathanOliver

24
หรือมันกำหนดผลลัพธ์ของ1 && i == 0?
JVApen

4
คำแนะนำสำหรับผู้เริ่มต้น: พวกเขาไม่ควรใช้โครงสร้างภาษา "ขั้นสูง" ดังกล่าว เพียงแค่กำหนดตัวแปรแยกต่างหาก ที่จะหลีกเลี่ยงปัญหาที่อาจเกิดขึ้นกับจุดลำดับ รหัสประเภทนี้ในรหัสที่ใช้งานได้มักจะดูไม่ดี
user202729

1
นี่จะจบลงด้วยคำถามสัมภาษณ์
RAZ_Muh_Taz

คำตอบ:


391

นี้จะทำอย่างไรกับความสำคัญประกอบ

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

ไม่ใช่

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

เพราะทั้งสอง&&และมีความสำคัญสูงกว่า== =สิ่งที่ได้ผลจริงๆคือ

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

ซึ่งกำหนดผลมาจากการ1 && (i == 0) iดังนั้นถ้าiเริ่มต้นที่0แล้วi == 0คือtrueเพื่อให้1 && trueเป็นtrue(หรือ1) แล้วได้รับการตั้งค่าi 1แล้วตั้งแต่เป็นจริงคุณป้อนถ้าบล็อกและพิมพ์ค่าที่คุณได้รับมอบหมายให้1i


2
@ JörgWMittagมันเท่ห์มาก ฉันชอบที่มันบังคับให้คุณใช้วงเล็บ
NathanOliver

โดยทั่วไปi = !i; if (i)เขียนอย่างถูกต้อง
Cacahuete Frito

@NathanOliver: ป้อมปราการเป็นภาษาที่เจ๋งมากและมีหลายสิ่งที่ถูกต้อง (ผู้ออกแบบนำคือ Guy L. Steele ดังนั้นจึงไม่แปลกใจที่นั่น) แต่น่าเสียดายที่มันไม่ได้ถูกหยิบขึ้นมาสำหรับการระดมทุน DARPA รอบสุดท้ายและจากนั้น Oracle ก็ mothballed
Jörg W Mittag

6
โดยธรรมชาติแล้วการขอคำเตือนจากผู้รวบรวมจะตรวจพบข้อผิดพลาดนั้น
Deduplicator

4
ภาษาใดก็ตามที่ไม่ถือว่า ints และ booleans นั้นจะเทียบเท่ากัน
rghome

16

สมมติว่ารหัสของคุณมีลักษณะเช่นนี้:

#include <iostream>
using namespace std;

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

จากนี้:

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

ประเมินว่า

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

และเพื่อให้มีการตั้งค่า i1


39
จำเป็นต้องใช้รหัสพิเศษหรือไม่ ดูเหมือนว่าจะค่อนข้างชัดเจนว่ากรณีนี้จะไม่เกิดขึ้น
Modelmat

13
ไม่เพียง แต่รหัสเสริมที่ไม่จำเป็น คำตอบล้มเหลวในการอธิบายความสำคัญของผู้ปฏิบัติงานอย่างชัดเจน
Francisco Zarabozo

34
เนื่องจากเราอยู่บนรถไฟ nitpick ... ฉันเห็นusing namespace std!
Mateen Ulhaq

8
มีรหัสพิเศษ - แต่ก็ยังไม่ได้รหัสที่ไม่ถูกต้อง และคำตอบยังคงถูกต้อง แน่นอนมันไม่ได้อธิบายความสำคัญของผู้ปฏิบัติงาน แต่ใครบางคนสามารถแนะนำให้เพิ่มได้แทนที่จะลงคะแนนทันที!
B Charles H

14
ว้าว -4 มีความรุนแรงการพิจารณาถึงคำตอบนี้ถูกต้องแม้ว่าอาจจะไม่เหมาะสมก็ตาม มันไม่ได้ขยายไปตามลำดับความสำคัญของโอเปอเรเตอร์เท่าคำตอบอื่น ๆ แต่มันพูดได้เพียงพอในบริบทของรหัสเพื่อให้ทุกคนที่คิดว่า=มาก่อน&&สามารถเห็นปัญหาได้ นอกจากนี้ใช่การขยายตัวนั้นไม่สำคัญ แต่ฉันไม่คิดว่ามันสำคัญมาก ฉันไม่สามารถเชื่อว่าความแตกต่างเล็ก ๆ น้อย ๆ นี้ทำให้คนลงคะแนน 151 ถึง -4
JoL

-4

มันเกี่ยวข้องกับการแยกวิเคราะห์กฎจากขวาไปซ้าย เช่น y = x + 5
การแสดงออกย่อยทั้งหมดมีความสำคัญ สองสำนวนที่มีความสำคัญเท่ากันจะถูกประเมินจากขวาไปซ้าย,. ด้าน && การแสดงออกจะทำก่อนจากนั้นตามด้วย LHS

ทำให้รู้สึกถึงฉัน


1
ความเกี่ยวข้อง ("จากขวาไปซ้ายกฎ") ไม่มีอะไรเกี่ยวข้องกับสิ่งนี้ มันเกี่ยวกับความสำคัญ ("สำคัญ") และตัวดำเนินการที่ใช้ไม่ได้มีความสำคัญเท่ากัน
การแข่งขัน Lightness ใน Orbit

-4

คำตอบที่แท้จริงคือ:

  1. คอมไพเลอร์ให้ความสำคัญกับ "i == 0" ซึ่งประเมินว่าเป็นจริง
  2. จากนั้นมันจะประเมินค่า i = 1 เป็น TRUE หรือ FALSE และเนื่องจากตัวดำเนินการที่ได้รับมอบหมายที่รวบรวมไว้จะไม่ล้มเหลว
  3. เนื่องจากทั้งสองข้อความประเมินว่าเป็นจริงและ TRUE && TRUE ประเมินเป็น TRUE ข้อความสั่ง if จะประเมินเป็น TRUE

เพื่อเป็นการพิสูจน์ให้ดูที่เอาต์พุต asm ของคอมไพเลอร์ของคุณสำหรับโค้ดที่คุณป้อน (ความคิดเห็นทั้งหมดเป็นของฉันเอง):

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

เอาต์พุต asm ด้านบนมาจาก CLANG แต่คอมไพเลอร์อื่น ๆ ทั้งหมดที่ฉันดูให้เอาต์พุตเหมือนกัน สิ่งนี้เป็นจริงสำหรับคอมไพเลอร์ทั้งหมดในไซต์นั้นไม่ว่าจะเป็นคอมไพเลอร์ C หรือ C ++ ล้วน แต่ไม่มี pragmas ใด ๆ เพื่อเปลี่ยนโหมดของคอมไพเลอร์ (โดยค่าเริ่มต้นคือ C ++ สำหรับคอมไพเลอร์ C ++)

โปรดทราบว่าคอมไพเลอร์ของคุณไม่ได้ตั้งค่า i = 1 แต่ i = TRUE (ซึ่งหมายถึงค่าจำนวนเต็ม 32- บิตไม่เป็นศูนย์) นั่นเป็นเพราะผู้ประกอบการ && ประเมินเฉพาะว่าข้อความเป็น TRUE หรือ FALSE จากนั้นตั้งค่าผลลัพธ์ตามผลลัพธ์ เพื่อพิสูจน์ลองเปลี่ยน i = 1 เป็น i = 2 และคุณสามารถสังเกตด้วยตัวคุณเองว่าไม่มีอะไรเปลี่ยนแปลง ดูตัวเองด้วยการใช้คอมไพเลอร์ออนไลน์ที่Compiler Explorer


1
1) ลิงก์ docs ไปยังลำดับความสำคัญของตัวดำเนินการ C เมื่อคำถามนี้ถูกแท็กด้วย C ++ สองภาษาที่แตกต่างกัน 2a) i = 1เป็นตัวดำเนินการ [ไม่ใช่ความเท่าเทียม] 2b) ฉันสามารถรับรองคุณได้ว่าif (i = 0)จะประเมินเงื่อนไขที่เป็นเท็จทั้งใน C และ C ++ ดังนั้นการประเมินว่าเป็น WRT จริงหรือไม่ "ไม่เคยล้มเหลว" จะทำให้เข้าใจผิด
TrebledJ

1
and cl, 1 ; = operator always TRUE<< แก้ไขฉันถ้าฉันผิด แต่ฉันไม่เห็นการมอบหมายที่นี่ เพราะมันหมายถึง1 &&ส่วนของการแสดงออก ดังนั้นคำตอบนี้ประเมินโดยทั่วไปfalseว่า
syck

"ลิงก์เอกสารไปยังลำดับความสำคัญของโอเปอเรเตอร์ C เมื่อคำถามนี้ถูกแท็กด้วย C ++ สองภาษาที่แตกต่างกัน" - และเมื่อคุณเปรียบเทียบลำดับความสำคัญของโอเปอเรเตอร์ของ C ถึง C ++ ความแตกต่างระหว่างทั้งสองคืออะไร พวกเขามีความสำคัญเหมือนกันในเรื่องที่เกี่ยวกับหัวข้อนี้ซึ่งไม่น่าแปลกใจเนื่องจาก C ++ เป็นอนุพันธ์โดยตรงของ C (หรืออีกวิธีหนึ่งที่จะกล่าวคือ C เป็นส่วนย่อยของภาษา C ++ ดังนั้นแน่นอนว่าพวกเขาจะมี เหมือนกันมากรวมถึงลำดับความสำคัญ) ฉันจะแก้ไขโพสต์ของฉันอย่างไรก็ตามในกรณีที่เกิดความสับสน
18

"แก้ไขฉันถ้าฉันผิด แต่ฉันไม่เห็นงานใด ๆ ที่นี่" - จากนั้นให้ฉันแก้ไขให้คุณ! 1 คือค่าทันทีและไม่ใช่ผลลัพธ์ของการทดสอบหรือการคำนวณใด ๆ มันคือสิ่งที่เรียกว่าค่า "สันนิษฐานว่าเป็นจริง" การทดสอบเพียงอย่างเดียวที่เกิดขึ้นสำหรับคำสั่ง i == 0 คือ - "cmp dword ptr [rbp - 8], 0" คุณจะถูกต้องก็ต่อเมื่อมันได้พูดว่า "movzx edx, 1" ตามโพสต์ทั้งหมดที่อยู่ก่อนหน้าของฉันควรมีการเปรียบเทียบสองแบบ แต่ในชีวิตจริงมีเพียงอันเดียวและเอาต์พุต asm ของคอมไพเลอร์สำคัญทุกคนพิสูจน์โพสต์เหล่านั้นไม่ถูกต้องสมบูรณ์
18

2
เช่นเดียวกับการได้รับลำดับความสำคัญผิด (ดูคำตอบของ NathanOliver สำหรับการแยกวิเคราะห์ที่ถูกต้อง) คุณทำการอ้างสิทธิ์ที่ผิดพลาดว่าผู้ดำเนินการที่ได้รับมอบหมายนั้นประเมินผลเป็น TRUE เสมอ ลองif ( i = 0 ) { print something }ดู คำตอบของคุณขัดแย้งกับตัวเอง ในตอนเริ่มต้นคุณบอกว่าi=1มีการประเมินผลก่อนที่จะ&&ถูกนำไปใช้และจากนั้นในตอนท้ายคุณบอกว่าiถูกตั้งค่าเป็นผลการ&&ดำเนินการ
MM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.