สำหรับ i = 0 ทำไม (i + = i ++) เท่ากับ 0


253

ใช้รหัสต่อไปนี้ (ใช้งานเป็นแอปพลิเคชันคอนโซล):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

ผลลัพธ์iคือ 0 ฉันคาดว่า 2 (เหมือนที่เพื่อนร่วมงานของฉันทำ) คอมไพเลอร์อาจสร้างโครงสร้างบางอย่างที่iเป็นศูนย์

เหตุผลที่ฉันคาดว่า 2 คือในความคิดของฉันคำสั่งทางขวาจะได้รับการประเมินก่อนโดยเพิ่มค่า i ด้วย 1 มากกว่าที่เพิ่มเข้ากับ i เนื่องจากฉันมีอยู่แล้ว 1 มันจึงเพิ่ม 1 เป็น 1 ดังนั้น 1 + 1 = 2 เห็นได้ชัดว่านี่ไม่ใช่สิ่งที่เกิดขึ้น

คุณช่วยอธิบายว่าคอมไพเลอร์ทำอะไรหรือเกิดอะไรขึ้นตอนรันไทม์? ทำไมผลลัพธ์เป็นศูนย์?

ข้อจำกัดความรับผิดชอบบางประการ: ฉันรู้ดีว่าคุณจะไม่ใช้รหัสนี้ (และอาจไม่ควร) ฉันรู้ว่าฉันจะไม่ทำ อย่างไรก็ตามฉันพบว่ามันเป็นเรื่องที่น่าสนใจที่จะรู้ว่าทำไมมันถึงทำแบบนั้นและเกิดอะไรขึ้นอย่างแน่นอน


57
ผลลัพธ์ที่คาดหวังไม่ควรเป็น 1 หรือไม่? i (0) + = i ++ (1) ดังนั้น 0 + = 1 = 1
aleation

11
มันทำงานได้ตามที่คาดไว้
Steve เป็น

177
คำถามนี้จะถามคำถามที่แตกต่างกันไปกี่แบบ?
mowwwalker

20
การเตรียมระบบล่วงหน้าจะเพิ่มค่าก่อนที่จะทำการ acction i + = ++ ฉันจะให้ 1
Pierluc SS

21
ทำไมทุกคนถึงเพ่งความสนใจไปที่การเตรียมความพร้อมก่อนหลัง? สิ่งที่ "แปลก" คือค่าของiทางด้านซ้ายของ+=คือ "แคช" ก่อนที่จะประเมินทางด้านขวามือ นี่คือการตอบโต้แบบง่าย ๆ อย่างเช่นต้องการการทำสำเนาหากiเป็นวัตถุ (โปรดอย่าเข้าใจฉันผิด: ฉันเห็นด้วยอย่างยิ่งที่จะระบุว่า0เป็นคำตอบที่ถูกต้องและเป็นไปตามมาตรฐาน)
JohnB

คำตอบ:


425

นี้:

int i = 0;
i += i++

สามารถมองเห็นได้ในขณะที่คุณทำ (ต่อไปนี้เป็นข้อสรุปรวมขั้นต้น):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

สิ่งที่เกิดขึ้นจริงมีส่วนร่วมมากกว่านั้น - ดูที่ MSDN, 7.5.9 Postfix ตัวเพิ่มและตัวดำเนินการลดค่า :

การประมวลผลแบบรันไทม์ของการเพิ่มหรือการลดขนาด postfix ของรูปแบบ x ++ หรือ x-- ประกอบด้วยขั้นตอนต่อไปนี้:

  • ถ้า x ถูกจัดประเภทเป็นตัวแปร:

    • x ถูกประเมินเพื่อสร้างตัวแปร
    • บันทึกค่าของ x
    • ผู้ประกอบการที่เลือกถูกเรียกด้วยค่าที่บันทึกไว้ของ x เป็นอาร์กิวเมนต์ของมัน
    • ค่าที่ส่งคืนโดยผู้ประกอบการจะถูกเก็บไว้ในสถานที่ที่กำหนดโดยการประเมินผลของ x
    • ค่าที่บันทึกไว้ของ x กลายเป็นผลลัพธ์ของการดำเนินการ

โปรดทราบว่าเนื่องจากลำดับความสำคัญเหนือกว่า postfix จะ++เกิดขึ้นก่อนหน้า +=แต่ผลลัพธ์ไม่ได้ถูกใช้ (เนื่องจากใช้ค่าก่อนหน้าiนี้)


การย่อยสลายอย่างละเอียดยิ่งขึ้นของi += i++ชิ้นส่วนที่ทำขึ้นมานั้นต้องใช้อย่างใดอย่างหนึ่งเพื่อให้รู้ว่าทั้งสอง+=และ++ไม่ได้เป็นอะตอม (นั่นคือไม่เป็นหนึ่งในการดำเนินงานเดียว) แม้ว่าพวกเขาจะดูเหมือน วิธีที่สิ่งเหล่านี้ถูกนำไปใช้เกี่ยวข้องกับตัวแปรชั่วคราวสำเนาiก่อนการดำเนินการ - หนึ่งสำหรับแต่ละการดำเนินการ (ฉันจะใช้ชื่อiAddและiAssignสำหรับตัวแปรชั่วคราวที่ใช้สำหรับ++และ+=ตามลำดับ)

ดังนั้นการประมาณสิ่งที่เกิดขึ้นใกล้เคียง:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;

3
@Oded การ++ดำเนินการเสร็จสิ้นก่อนที่การประเมินผลของคำสั่งจะเสร็จสมบูรณ์ ดังนั้น+=เขียนทับค่า เกิดอะไรขึ้นเหรอ?
Anirudh Ramanathan

6
@Oded int i = 0; i = i + 1; (postfix) i = 0; (assignment)จริงของมัน หากคุณใช้ i ที่อื่นในคำสั่งนั้นจะประเมินเป็น 1 ในเวลานั้น
drch

@Cthulhu - เป็นหลัก คำตอบโดยDTBไปในรายละเอียด
Oded

6
ฉันไม่ซื้ออันนี้ คำตอบของ @yoriy นั้นแม่นยำกว่ามาก สำหรับหนึ่งในคำตอบของคุณคุณบอกว่าบรรทัดสุดท้ายจะเป็นในขณะที่มันควรจะเป็นi+1 i=i+1นั่นi++คืออะไร
recluze

3
ส่วนแรกของคำตอบคือซ้ำซ้อน ตัวอย่างรหัสสุดท้ายของคุณสามารถทำได้ IMHO +1 แม้ว่า
corazza

194

ถอดชิ้นส่วนของรหัสที่ทำงาน:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

รหัสเทียบเท่า

มันจะรวบรวมรหัสเดียวกันกับรหัสต่อไปนี้:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

การถอดรหัสที่สอง (เพื่อพิสูจน์ว่าเหมือนกัน)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

เปิดหน้าต่างถอดแยกชิ้นส่วน

คนส่วนใหญ่ไม่ทราบหรือจำไม่ได้ว่าพวกเขาสามารถเห็นรหัสประกอบในหน่วยความจำสุดท้ายโดยใช้หน้าต่างVisual Studio Disassembly จะแสดงรหัสเครื่องที่กำลังดำเนินการไม่ใช่ CIL

ใช้สิ่งนี้ขณะทำการดีบั๊ก:

Debug (menu) -> Windows (submenu) -> Disassembly

แล้วจะเกิดอะไรขึ้นกับ postfix ++

postfix ++ บอกว่าเราต้องการที่จะเพิ่มมูลค่าของตัวถูกดำเนินการหลังจากการประเมินผลที่ ... ที่ทุกคนรู้ว่า ... สิ่งที่สร้างความสับสนเล็กน้อยคือความหมายของ"หลังจากการประเมินผล"

ดังนั้น"หลังจากการประเมินผล"หมายความว่าอะไร:

  • ประเพณีอื่น ๆ ของตัวถูกดำเนินการในบรรทัดรหัสเดียวกันจะต้องได้รับผลกระทบ:
    • a = i++ + i i ที่สองได้รับผลกระทบจากการเพิ่มขึ้น
    • Func(i++, i) ฉันที่สองได้รับผลกระทบ
  • ประเพณีอื่น ๆ ในสายเดียวกันเคารพผู้ประกอบการลัดวงจรเช่น||และ&&:
    • (false && i++ != i) || i == 0 i อันที่สามไม่ได้รับผลกระทบจาก i ++ เนื่องจากไม่ได้รับการประเมิน

ดังนั้นความหมายของi += i++;คืออะไร:

มันเป็นเช่นเดียวกับ i = i + i++;

ลำดับการประเมินคือ:

  1. เก็บ i + i (นั่นคือ 0 + 0)
  2. เพิ่มค่า i (i กลายเป็น 1)
  3. กำหนดค่าของขั้นตอนที่ 1 ถึง i (i กลายเป็น 0)

ไม่ใช่ว่าการเพิ่มขึ้นจะถูกยกเลิก

ความหมายของi = i++ + i;อะไร:

นี่ไม่ใช่ตัวอย่างก่อนหน้านี้ ที่สามiได้รับผลกระทบจากการเพิ่มขึ้น

ลำดับการประเมินคือ:

  1. ร้านค้า i (นั่นคือ 0)
  2. เพิ่มค่า i (i กลายเป็น 1)
  3. มูลค่าการจัดเก็บของขั้นตอนที่ 1 + i (นั่นคือ 0 + 1)
  4. กำหนดค่าของขั้นตอน 3 ถึง i (i กลายเป็น 1)

22
+ 1 ++ - สำหรับการผ่าไม่ยอมใครง่ายๆที่แท้จริง เชย Norris จะภูมิใจ :) ผมคิดว่าคุณจะทำให้สมมติฐานที่ว่า OP อยู่ในอินเทลไม่ได้เป็นพอร์ตโมโนแม้ว่า ...
StuartLC

19
C # มีลำดับการประเมินที่กำหนดไว้อย่างชัดเจนสำหรับนิพจน์และรหัสวัตถุเพียงแค่ดำเนินการตามลำดับนั้น เอาท์พุทรหัสเครื่องไม่ใช่เหตุผลหรือคำอธิบายสำหรับลำดับการประเมิน
Kaz

8
รหัสเครื่องทำให้ง่ายต่อการเข้าใจวิธีการใช้งานลำดับการประเมินผล IMO
เควิน

5
@ StuartLC ฉันเห็นสิ่งที่คุณทำที่นั่น ความอัปยศเกี่ยวกับการยกเลิกโหวตว่า
Steffan Donal

2
a++ + aไม่เหมือนกับa + a++เพราะนี่ไม่ใช่คณิตศาสตร์บริสุทธิ์อีกต่อไป กฎการสับเปลี่ยนกำลังพลในพีชคณิตไม่ได้คำนึงถึงความเป็นไปได้ที่ตัวแปรจะเปลี่ยนค่ากลางคันผ่านนิพจน์ คณิตศาสตร์จะแมปกับการเขียนโปรแกรมอย่างเรียบร้อยเฉพาะเมื่อการเขียนโปรแกรมเป็นการเขียนโปรแกรมที่ใช้งานได้ และยังไม่ถึงตอนนั้นเพราะมีข้อ จำกัด ในการดำเนินการ เช่นบางครั้งจำนวนจุดลอยตัวจะทำงานเหมือน reals และบางครั้งก็ไม่ แม้จะไม่มีผลข้างเคียงกฎการสับเปลี่ยนและกฎการเชื่อมโยงที่ใช้กับจำนวนจริงในการคำนวณทางคณิตศาสตร์กับจำนวนจุดลอยตัว
Kaz

61
int i = 0;
i += i++;

มีการประเมินดังนี้:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

ie iมีการเปลี่ยนแปลงสองครั้ง: ครั้งหนึ่งโดยการi++แสดงออกและอีกครั้งโดย+=คำสั่ง

แต่ตัวถูกดำเนินการของ+=คำสั่งคือ

  • ค่าiก่อนการประเมินผลi++(ด้านซ้ายมือ+=) และ
  • ค่าiก่อนการประเมินผลของi++(ด้านขวามือของ+=)

อานี่เป็นคำอธิบายที่น่าอัศจรรย์ เตือนฉันเมื่อฉันทำงานกับเครื่องคิดเลขที่ใช้กองซ้อนโดยใช้สัญลักษณ์ขัดเงาย้อนกลับ
นาธาน

36

ขั้นแรกให้i++ผลตอบแทน 0 จากนั้นiจะเพิ่มขึ้นทีละ 1 สุดท้ายiจะถูกตั้งค่าเป็นค่าเริ่มต้นiซึ่งเท่ากับ 0 บวกค่าที่i++ส่งคืนซึ่งเป็นศูนย์ด้วย 0 + 0 = 0


2
แต่i += i++;ไม่ใช่i = i++;เพราะมีการเพิ่มค่าของi++(0) iและไม่ใช่ " iถูกตั้งค่าเป็นค่าที่i++ส่งคืน" ตอนนี้คำถามคือเมื่อเพิ่มค่าที่i++ส่งคืนไปiจะiเป็นค่าที่เพิ่มขึ้นหรือค่าที่ไม่เพิ่มขึ้น? คำตอบเพื่อนของฉันถูกเขียนในรายละเอียด
Daniel Fischer

จริงฉันจะแก้ไขมัน แต่อย่างไรก็ตามตั้งแต่i = 0แรกi += somethingเทียบเท่ากับซึ่งเป็นi = 0 + something i = something
Jong

32

นี่เป็นเพียงการประเมินจากซ้ายไปขวาและจากล่างขึ้นบนของโครงสร้างไวยากรณ์แบบนามธรรม ตามแนวคิดต้นไม้ของนิพจน์นั้นเดินจากบนลงล่าง แต่การประเมินผลจะเปิดออกเมื่อการเรียกซ้ำปรากฏขึ้นสำรองต้นไม้จากด้านล่าง

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

การประเมินเริ่มต้นด้วยการพิจารณาโหนดรู+=ท นั่นคือองค์ประกอบที่สำคัญของการแสดงออก ตัวถูกดำเนินการด้านซ้ายของ+=จะต้องได้รับการประเมินเพื่อกำหนดสถานที่ที่เราเก็บตัวแปรและเพื่อให้ได้ค่าก่อนซึ่งเป็นศูนย์ ถัดไปจะต้องประเมินด้านขวา

ด้านขวาเป็นโพสต์ที่เพิ่มขึ้น++ผู้ประกอบการ มันมีหนึ่งตัวถูกดำเนินการiซึ่งมีการประเมินทั้งเป็นแหล่งที่มาของค่าและเป็นสถานที่ที่ค่าจะถูกเก็บไว้ ผู้ประกอบการประเมินiค้นหา0และจัดเก็บจึงเป็น1สถานที่ มันคืนค่าก่อนหน้า0ตามความหมายของการคืนค่าก่อน

ตอนนี้การควบคุมจะกลับไปที่+=ผู้ควบคุม ตอนนี้มีข้อมูลทั้งหมดเพื่อให้การดำเนินการเสร็จสมบูรณ์ มันรู้สถานที่ที่จะเก็บผล (ตำแหน่งที่เก็บของi) 0เช่นเดียวกับค่าก่อนและจะมีค่าที่จะเพิ่มเข้าไปในค่าก่อนคือ ดังนั้นiจบลงด้วยศูนย์

เช่น Java, C # ทำให้ลักษณะของภาษา C ถูกทำให้สะอาดโดยการกำหนดลำดับการประเมิน จากซ้ายไปขวา, จากล่างขึ้นบน: ลำดับที่ชัดเจนที่สุดที่ผู้เขียนโค้ดคาดหวัง


+1: ฉันเห็นด้วยกับคุณยกเว้นในกรณีที่ผู้ทำโค้ดทุกคนคาดหวังว่า ... ฉันคาดว่ามันจะเหมือนกับสิ่งนี้: SetSum(ref i, Inc(ref i))กับint SetSum(ref int a, int b) { return a += b; }และint Inc(ref int a) { return a++; }... แน่นอนฉันไม่คาดหวังอีกต่อไป
Miguel Angelo

นอกจากนี้สิ่งที่ฉันคาดหวังไม่สอดคล้องกัน! มันจะไม่เท่ากับSet(ref i, Sum(i, Inc(ref i)))ที่มีและint Set(ref int a, int b) { return a = b; } int Sum(int a, int b) { return a + b; }
Miguel Angelo

ขอบคุณ; คุณแบะท่าข้อบกพร่อง / ความไม่สมบูรณ์ในคำตอบของฉันที่ฉันต้องแก้ไข
Kaz

ปัญหาที่เกิดขึ้นSetSumคือมันไม่ได้ประเมินค่าตัวถูกดำเนินการทางซ้ายiแต่ใช้ที่อยู่ของมันเท่านั้นดังนั้นจึงไม่เท่ากับการประเมินตัวถูกดำเนินการจากซ้ายไปขวาที่สมบูรณ์ SetSum(ref i, i, PostInc(ref i))คุณจำเป็นต้องมีสิ่งที่ต้องการ อาร์กิวเมนต์ที่สองของSetSumคุ้มค่าที่จะเพิ่มที่เราเพียงแค่ใช้ในการระบุค่าก่อนi เป็นเพียง iSetSumint SetSum(ref int dest, int a, int b) { return dest = a + b; }
Kaz

ความสับสนเกิดขึ้น (อย่างน้อยสำหรับฉัน) กับตัวดำเนินการ + = เนื่องจากตัวดำเนินการกำหนดค่ามีการประเมินจากขวาไปซ้าย (เช่น a = b = c = d) ... ดังนั้นเราจึงจินตนาการได้ว่า + = เป็นไปตามกฎเดียวกัน เป็นการทำงานแบบปรมาณู (อย่างที่ฉันทำกับวิธี SetSum ของฉัน) ... แต่สิ่งที่เกิดขึ้นในความเป็นจริงก็คือ C # แปลa += bเป็นa = a + b... แสดงว่าตัวดำเนินการ + = ไม่ใช่อะตอม ... มันเป็นแค่น้ำตาลซินแทคติค
Miguel Angelo

30

เพราะi++ก่อนส่งกลับค่าจากนั้นเพิ่มขึ้น แต่หลังจากที่ฉันถูกตั้งค่าเป็น 1 คุณจะตั้งค่ากลับเป็น 0


17

วิธีการเพิ่มภายหลังมีลักษณะดังนี้

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

ดังนั้นโดยทั่วไปเมื่อคุณโทรi++, iเป็นเพิ่มขึ้น แต่ค่าเดิมจะถูกส่งกลับในกรณีของคุณก็ถูกส่งกลับ 0


12

คำตอบง่ายๆ

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;

12

i ++ หมายถึง: คืนค่า i จากนั้นเพิ่มขึ้น

i + = i ++ หมายถึง: นำค่าปัจจุบันของ i เพิ่มผลลัพธ์ของ i ++

ทีนี้มาเพิ่ม i = 0 เป็นเงื่อนไขเริ่มต้น i + = i ++ ถูกประเมินเช่นนี้:

  1. ค่าปัจจุบันของ i คืออะไร มันคือ 0 เก็บไว้เพื่อให้เราสามารถเพิ่มผลลัพธ์ของ i ++ ลงไปได้
  2. หาค่า i ++ (หาค่าเป็น 0 เพราะนั่นคือค่าปัจจุบันของ i)
  3. โหลดค่าที่เก็บไว้และเพิ่มผลลัพธ์ของขั้นตอนที่ 2 ลงไป (เพิ่ม 0 ถึง 0)

หมายเหตุ: ในตอนท้ายของขั้นตอนที่ 2 ค่าของ i คือจริง 1 อย่างไรก็ตามในขั้นตอนที่ 3 คุณต้องละทิ้งมันโดยการโหลดค่าของ i ก่อนที่จะเพิ่มขึ้น

ตรงข้ามกับ i ++, ++ i ส่งคืนค่าที่เพิ่มขึ้น

ดังนั้นฉัน + = ++ ฉันจะให้คุณ 1


มันเป็นความช่วยเหลืออย่างเต็มรูปแบบ
sonha

11

ตัวดำเนินการที่เพิ่มขึ้นหลังการแก้ไข++ให้ค่าตัวแปรในนิพจน์แล้วทำการเพิ่มที่คุณกำหนดให้ส่งกลับค่าศูนย์ (0) ไปที่iอีกครั้งซึ่งจะเขียนทับค่าที่เพิ่มขึ้นหนึ่ง (1)ดังนั้นคุณจึงได้รับศูนย์ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับโอเปอเรเตอร์ที่เพิ่มขึ้นใน++ โอเปอเรเตอร์ (MSDN)


8

i += i++;จะเท่ากับศูนย์เพราะมันทำ++หลังจากนั้น

i += ++i; จะทำมันก่อน


4
หาก++หลังจากนั้นฉันก็คาดหวังว่าผลลัพธ์จะออก1มา
comecme

8

++ postfix iจะประเมินผลก่อนการเพิ่มและ+=จะประเมินเพียงiครั้งเดียว

ดังนั้น, 0 + 0 = 0 เป็นiได้รับการประเมินและใช้งานก่อนที่จะเพิ่มขึ้นในขณะที่รูปแบบของ postfix ++ถูกนำมาใช้ หากต้องการiเพิ่มค่าก่อนให้ใช้แบบฟอร์มคำนำหน้า ( ++i)

(นอกจากนี้เพียงแค่ทราบ: คุณควรได้รับ 1 เพียงเป็น 0 + (0 + 1) = 1)

การอ้างอิง: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)


8

สิ่งที่ C # กำลังทำอยู่และ "ทำไม" ของความสับสน

ฉันยังคาดหวังว่าค่านี้จะเป็น 1 ... แต่การสำรวจบางอย่างเกี่ยวกับเรื่องนั้นก็ชี้แจงบางประเด็น

Cosider วิธีการดังต่อไปนี้:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

ผมคาดหวังว่าจะเป็นเช่นเดียวกับi += i++ SetSum(ref i, Inc(ref i))ค่าของ i หลังจากข้อความนี้คือ1 :

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

แต่แล้วฉันก็มาถึงข้อสรุปอื่น ... i += i++จริง ๆ แล้วเหมือนกับi = i + i++... ดังนั้นฉันจึงได้สร้างอีกตัวอย่างที่คล้ายกันโดยใช้ฟังก์ชั่นเหล่านี้:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

หลังจากเรียกสิ่งนี้Set(ref i, Sum(i, Inc(ref i)))ค่าของ i คือ0 :

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

สิ่งนี้ไม่เพียง แต่อธิบายว่า C # กำลังทำอะไร ... แต่ทำไมผู้คนมากมายถึงสับสนกับมัน ... รวมถึงฉันด้วย


2
โปรดเพิ่มสิ่งนี้ลงในคำตอบเดิมของคุณ แต่จะไม่เพิ่มผลประโยชน์ใด ๆ เพื่อให้ได้คำตอบแยก
casperOne

2
ฉันทำอย่างนี้เพื่อไม่ให้สุภาพคำตอบอื่น ๆ เนื่องจากเป็นเรื่องเกี่ยวกับโค้ดที่ถอดรหัสแล้ว ... ในขณะนี้ฉันพยายามใช้วิธีการอื่นในการอธิบายสิ่งต่าง ๆ คุณคิดอย่างไร? ฉันควรแก้ไขคำตอบอื่นและต่อท้ายคำตอบนี้หรือไม่ อาจจะเป็นการเพิ่มอันนี้ ... ไม่รู้สิ! ขอบคุณสำหรับคำแนะนำ!
Miguel Angelo

7

ความทรงจำที่ดีที่ฉันจำได้เสมอเกี่ยวกับเรื่องนี้คือ:

หาก++ยืนหลังจากการแสดงออกก็จะส่งกลับค่าที่มันเป็นก่อน ดังนั้นรหัสต่อไปนี้

int a = 1;
int b = a++;

1 เพราะaเป็น 1 ก่อนที่จะมีการเพิ่มขึ้นโดย++ยืนหลัง aคนเรียกสัญกรณ์การโพสต์นี้ นอกจากนี้ยังมีสัญกรณ์การแก้ไขปัญหาล่วงหน้าโดยที่สิ่งต่าง ๆ อยู่ตรงข้ามกัน: หาก++อยู่ก่อนนิพจน์จะส่งคืนค่าที่อยู่หลังการดำเนินการ:

int a = 1;
int b = ++a;

b เป็นสองในที่นี่

ดังนั้นสำหรับรหัสของคุณนี่หมายถึง

int i = 0;
i += (i++);

i++กลับ 0 (ตามที่กล่าวไว้ข้างต้น) 0 + 0 = 0ดังนั้น

i += (++i); // Here 'i' would become two

Scott Meyersอธิบายความแตกต่างระหว่างสองเครื่องหมายใน "การเขียนโปรแกรม C ++ ที่มีประสิทธิภาพ" ภายในi++(postfix) จำค่าiได้และเรียกคำนำหน้าสัญกรณ์ ( ++i) iและส่งกลับค่าเก่า นี่คือเหตุผลที่คุณควรใช้ allways ++iในforลูป (แม้ว่าฉันคิดว่าทุกคอมไพเลอร์ที่ทันสมัยแปลi++ไป++iในforลูป)


1
ฉันทดสอบint i = 0; i += (++i)และiตั้งค่าเป็นหนึ่งมากกว่าสอง มันทำให้รู้สึกถึงฉันเกินไปเนื่องจากการใช้คำนำหน้าแทน postfix ไม่เปลี่ยนแปลงความจริงที่ว่าถ้าคุณเขียนi += (++i)ออกไปi = i + (++i)ที่iจะประเมินก่อนที่จะ++iส่งผลให้ในและท้ายที่สุดi = 0 + (++i) i = 0 + 1
Wutz

6

คำตอบเดียวสำหรับคำถามของคุณที่ถูกต้องคือ: เพราะมันไม่ได้กำหนด

ตกลงก่อนที่คุณจะเผาฉันทั้งหมด ..

คุณทุกคนตอบว่าทำไมi+=i++เป็น ok i=0และตรรกะที่จะส่งผลในการ

ฉันถูกล่อลวงให้ลงคะแนนแต่ละคำตอบของคุณทุกคน แต่ชื่อเสียงที่ฉันคำนวณมานั้นสูงเกินไป ..

ทำไมฉันถึงคลั่งไคล้คุณ ไม่ใช่เพราะสิ่งที่คำตอบของคุณอธิบาย ..
ฉันหมายถึงทุกคำตอบที่ฉันอ่านได้พยายามอย่างยอดเยี่ยมในการอธิบายสิ่งที่เป็นไปไม่ได้ฉันปรบมือ!

แต่ผลลัพธ์คืออะไร ?? มันเป็นผลที่ใช้งานง่าย - มันเป็นผลที่ยอมรับได้หรือไม่?

คุณแต่ละคนเห็นว่า "ราชาที่เปลือยเปล่า" และยอมรับว่าเป็นกษัตริย์ที่มีเหตุผล

คุณผิดทั้งหมด!

i+=i++;ส่งผลให้ใน0ไม่ได้กำหนด

ข้อผิดพลาดในกลไกการประเมินผลภาษาถ้าคุณจะ .. หรือแย่กว่านั้น! ข้อบกพร่องในการออกแบบ

ต้องการพิสูจน์ไหม แน่นอนคุณต้องการ!

int t=0; int i=0; t+=i++; //t=0; i=1

ตอนนี้ ... เป็นผลลัพธ์ที่เข้าใจง่าย! เพราะเราประเมินครั้งแรกtมอบหมายให้มันด้วยค่าและหลังจากการประเมินและการมอบหมายเรามีการดำเนินการโพสต์แล้ว - เหตุผลไม่ใช่หรือ

มันมีเหตุผลว่า: i=i++และi=iให้ผลลัพธ์เดียวกันiหรือไม่

ในขณะที่t=i++และมีผลที่แตกต่างกันสำหรับt=ii

การดำเนินการภายหลังเป็นสิ่งที่ควรเกิดขึ้นหลังจากการประเมินผลข้อความ
ดังนั้น:

int i=0;
i+=i++;

ควรเหมือนกันถ้าเราเขียนว่า:

int i=0;
i = i + i ++;

และเช่นเดียวกับ:

int i=0;
i= i + i;
i ++;

และเช่นเดียวกับ:

int i=0;
i = i + i;
i = i + 1;

ผลลัพธ์ใด ๆ ที่ไม่ได้1บ่งบอกถึงข้อบกพร่องในการเขียนหรือข้อบกพร่องในการออกแบบภาษาถ้าเราไปด้วยการคิดอย่างมีเหตุผล - อย่างไรก็ตาม MSDN และแหล่งข้อมูลอื่น ๆ บอกเราว่า "เฮ้ - นี่คือไม่ได้กำหนด!"

ตอนนี้ก่อนที่ฉันจะดำเนินการต่อไปแม้ตัวอย่างชุดนี้ฉันไม่ได้รับการสนับสนุนหรือเป็นที่ยอมรับจากใครก็ตาม .. อย่างไรก็ตามนี่คือสิ่งที่เป็นไปตามสัญชาตญาณและเหตุผลที่ควรได้รับผล

ผู้เขียนโค้ดไม่ควรมีความรู้ในการเขียนหรือแปลชุดประกอบ!

หากมีการเขียนในลักษณะที่จะไม่เคารพคำจำกัดความภาษา - มันเป็นข้อผิดพลาด!

และจะเสร็จสิ้นผมคัดลอกจากวิกิพีเดียเพิ่มและผู้ประกอบการลดลง :
ตั้งแต่ปรับเปลี่ยนเพิ่มขึ้น / ลดลงผู้ประกอบการถูกดำเนินการของการใช้ดังกล่าวถูกดำเนินการมากกว่าหนึ่งครั้งภายในแสดงออกเดียวกันสามารถให้ผลลัพธ์ที่ไม่ได้กำหนด ตัวอย่างเช่นในนิพจน์เช่น x - ++ x จะไม่ชัดเจนในลำดับที่ควรดำเนินการตัวดำเนินการลบและส่วนเพิ่ม สถานการณ์เช่นนี้จะยิ่งแย่ลงเมื่อคอมไพเลอร์ถูกปรับให้เหมาะสมซึ่งอาจส่งผลให้ลำดับการดำเนินการของการดำเนินการแตกต่างจากที่โปรแกรมเมอร์ตั้งใจไว้

และดังนั้นจึง.

คำตอบที่ถูกต้องคือไม่ควรใช้สิ่งนี้! (ตามที่ไม่ได้ระบุไว้!)

ใช่ .. - มันมีผลลัพธ์ที่คาดเดาไม่ได้แม้ว่า C # พยายามที่จะทำให้เป็นมาตรฐาน

ฉันไม่พบเอกสารของ C # ที่อธิบายพฤติกรรมของคุณทุกคนซึ่งบันทึกไว้ว่าเป็นพฤติกรรมปกติหรือที่กำหนดไว้อย่างดีของภาษา สิ่งที่ฉันพบคือสิ่งที่ตรงกันข้ามแน่นอน!

[ คัดลอกมาจากเอกสาร MSDN สำหรับ Postfix Increment and Decrement Operators: ++ และ - ]

เมื่อผู้ประกอบการ postfix ถูกนำไปใช้กับฟังก์ชั่นการโต้แย้งค่าของการโต้แย้งไม่รับประกันว่าจะเพิ่มขึ้นหรือลดลงก่อนที่มันจะถูกส่งผ่านไปยังฟังก์ชั่น ดูส่วน 1.9.17 ในมาตรฐาน C ++ สำหรับข้อมูลเพิ่มเติม

สังเกตคำที่ไม่รับประกัน ...

ยกโทษให้ฉันถ้าคำตอบที่ดูเหมือนหยิ่ง - ฉันไม่ใช่คนหยิ่ง ฉันแค่พิจารณาว่าผู้คนหลายพันคนมาที่นี่เพื่อเรียนรู้และคำตอบที่ฉันอ่านจะทำให้พวกเขาเข้าใจผิดและจะเป็นอันตรายต่อตรรกะและความเข้าใจในเรื่องของพวกเขา


ฉันไม่แน่ใจว่าฉันติดตาม 100% แต่คุณอ้างอิงเอกสาร C ++ แต่คำถามของฉันเกี่ยวกับ C # เอกสารเกี่ยวกับที่อยู่ที่นี่
ปีเตอร์

ฉันหมายถึง C # ในคำตอบของฉัน จากลิงก์ที่คุณระบุ: ผลลัพธ์ของ x ++ หรือ x-- คือค่าของ x ก่อนการดำเนินการในขณะที่ผลลัพธ์ของ ++ x หรือ --x คือค่าของ x หลังจากการดำเนินการ ในกรณีใดกรณีหนึ่ง x ตัวเองมีค่าเดียวกันหลังจากการดำเนินการ แสดงให้เห็นชัดเจนนี้ไม่ได้กรณีที่เมื่อการทดสอบ .. เพราะจะให้ผลที่แตกต่างจากi=++i i=i++ดังนั้นคำตอบของฉันยืนอยู่
GY

อ๊ะโอเค แต่มันสับสนระหว่างอ้างอิงเอกสาร C ++ ดังนั้นสิ่งที่คุณพูดคือสเปคไม่ได้ดำเนินการอย่างถูกต้อง?
Peter

ไม่สิ่งที่ฉันพูดคือมันไม่ได้กำหนดตามข้อกำหนดและการใช้งานที่ไม่ได้กำหนดจะสิ้นสุดในผลลัพธ์ที่ไม่ได้กำหนด
GY

ไม่ได้กำหนดใน C ++ แต่ C # บอกว่าควรเป็นค่าเดียวกันหลังจากการดำเนินการใช่ไหม? นั่นไม่เหมือนกับที่ไม่ได้กำหนด (แต่ฉันเห็นด้วยว่าคุณไม่ควรใช้ดูข้อจำกัดความรับผิดชอบของฉันฉันแค่พยายามที่จะเข้าใจว่าเกิดอะไรขึ้น)
ปีเตอร์

4

ผู้ประกอบการ ++ หลังจากตัวแปรทำให้มันเพิ่มขึ้น postfix การเพิ่มขึ้นเกิดขึ้นหลังจากทุกสิ่งในคำสั่งการเพิ่มและการมอบหมาย ถ้าคุณใส่เครื่องหมาย + + แทนตัวแปรมันจะเกิดขึ้นก่อนที่ค่าของ i จะถูกประเมินและให้คำตอบที่คาดหวัง


2
++ไม่ได้เกิดขึ้นหลังจาก+=คำสั่งก็จะเกิดขึ้นในระหว่าง การดำเนินการของ+=คำสั่ง นั่นเป็นเหตุผลที่ผลกระทบจากการที่ได้รับการแทนที่โดย++ +=
dtb

การใช้ ++ ฉันจะให้ผลลัพธ์เป็น 1 ไม่ใช่ใน 2 (เดิมคือ 'คำตอบที่คาดหมาย' ของฉัน)
ปีเตอร์

ดูเหมือนว่าการมอบหมาย+= จะเขียนทับการปรับเปลี่ยนเนื่องจากการเพิ่มขึ้นก่อนหรือหลังในการแสดงออก
Steven Lu

4

ขั้นตอนในการคำนวณคือ:

  1. int i=0 // เริ่มต้นเป็น 0
  2. i+=i++ // สมการ
  3. i=i+i++ // หลังจากทำให้สมการเรียบง่ายโดยคอมไพเลอร์
  4. i=0+i++ // i การทดแทนค่า
  5. i=0+0 // i ++ คือ 0 ตามที่อธิบายไว้ด้านล่าง
  6. i=0 // ผลสุดท้าย i = 0

ที่นี่ค่าเริ่มต้นiคือ 0 WKT i++ไม่มีอะไร แต่: ใช้iค่าก่อนแล้วจึงเพิ่มiค่าเป็น 1 ดังนั้นจึงใช้iค่าเป็น 0 ในขณะที่คำนวณi++แล้วเพิ่มค่าทีละ 1 ดังนั้นผลลัพธ์จะมีค่า จาก 0


3

มีสองตัวเลือก:

ตัวเลือกแรก: ถ้าคอมไพเลอร์อ่านคำสั่งดังต่อไปนี้

i++;
i+=i;

ดังนั้นผลลัพธ์คือ 2

สำหรับ

else if
i+=0;
i++;

ผลลัพธ์ที่ได้คือ 1


5
ซึ่งไม่เป็นผลลัพธ์ที่แท้จริง
Steven Lu

3

ระวังให้มาก: อ่านคำถามที่พบบ่อย C : สิ่งที่คุณพยายามทำ (การผสมการมอบหมายและ++ตัวแปรเดียวกัน) ไม่เพียง แต่ไม่ได้ระบุ แต่ยังไม่ได้กำหนด (หมายความว่าคอมไพเลอร์อาจทำทุกอย่างเมื่อประเมิน! ผลลัพธ์ "ที่สมเหตุสมผล")

โปรดอ่านมาตรา 3 ส่วนทั้งหมดมีมูลค่าการอ่าน! โดยเฉพาะอย่างยิ่ง 3.9 ซึ่งอธิบายความหมายของการไม่ระบุ ส่วนที่ 3.3 ให้ข้อมูลสรุปเกี่ยวกับสิ่งที่คุณสามารถทำได้และไม่สามารถทำได้ด้วย "i ++" และสิ่งที่คล้ายกัน

คุณอาจได้รับ 0 หรือ 2 หรือ 1 หรือแม้แต่อย่างอื่นทั้งนี้ขึ้นอยู่กับผู้รวบรวมภายใน และเนื่องจากมันไม่ได้กำหนดจึงเป็นเรื่องดีสำหรับพวกเขาที่จะทำเช่นนั้น


โอ๊ะ c # ... ฉันถูก "gcc" ทิ้งไปบางส่วนก็ผ่านการแยกรหัส
Olivier Dulac

1
ฉันพลาดมันคือ C # เหมือนกัน แต่ก็ชอบคำตอบอยู่ดี
เลนคอลลินส์

1
@Iain: ขอบคุณฉันก็เชื่อว่ามันคุ้มค่าที่จะรักษาคำตอบที่มีอยู่หลายคนไม่ทราบเกี่ยวกับเรื่องนี้ (หรือเกี่ยวกับคำถามที่พบบ่อยที่ดีจากเวลาที่ดีที่สุดของ Usenet ที่คนส่วนใหญ่มีความรู้เกี่ยวกับ subjcet สถานที่ที่จะอัปเดต)
Olivier Dulac

3

มีเหตุผลที่ยอดเยี่ยมมากมายในคำตอบข้างต้นฉันเพิ่งทำแบบทดสอบเล็ก ๆ และต้องการแบ่งปันกับคุณ

int i = 0;
i+ = i++;

ผลลัพธ์นี่ฉันแสดง 0 ผลลัพธ์ พิจารณากรณีด้านล่าง:

กรณีที่ 1:

i = i++ + i; //Answer 1

ก่อนหน้านี้ฉันคิดว่าโค้ดด้านบนมีลักษณะคล้ายกับนี้ดังนั้นคำตอบที่ค้นหาครั้งแรกคือ 1 และคำตอบที่แท้จริงของ i สำหรับอันนี้คือ 1

กรณีที่ 2:

i = i + i++; //Answer 0 this resembles the question code.

ผู้ประกอบการที่เพิ่มขึ้นที่นี่ไม่ได้มาในเส้นทางการดำเนินการซึ่งแตกต่างจากกรณีก่อนหน้านี้ที่ i ++ มีโอกาสที่จะดำเนินการก่อนที่จะเพิ่ม

ฉันหวังว่านี่จะช่วยได้เล็กน้อย ขอบคุณ


2

หวังว่าจะตอบคำถามนี้จากเปอร์สเปคทีฟของการเขียนโปรแกรม C 101

ดูเหมือนว่าฉันจะเกิดขึ้นในคำสั่งนี้:

  1. iถูกประเมินเป็น 0 ซึ่งส่งผลให้i = 0 + 0มีการดำเนินการเพิ่มขึ้นi++"เข้าคิว" แต่ได้รับมอบหมายเป็น 0 ถึงiยังไม่เกิดขึ้น
  2. การเพิ่มขึ้น i++ขึ้นเกิดขึ้น
  3. การมอบหมายi = 0จากด้านบนเกิดขึ้นเขียนทับสิ่งที่ # 2 (การเพิ่มภายหลัง) อย่างมีประสิทธิภาพ

ตอนนี้ # 2 อาจไม่เคยเกิดขึ้นจริง (อาจไม่ได้) เพราะคอมไพเลอร์มีแนวโน้มที่จะรู้ตัวว่ามันจะไม่มีจุดประสงค์ แต่สิ่งนี้อาจขึ้นอยู่กับคอมไพเลอร์ ไม่ว่าจะด้วยวิธีใดคำตอบที่มีความรู้มากกว่านี้แสดงให้เห็นว่าผลลัพธ์นั้นถูกต้องและเป็นไปตามมาตรฐาน C # แต่ก็ไม่ได้กำหนดว่าจะเกิดอะไรขึ้นกับ C / C ++

เป็นอย่างไรและทำไมเกินความเชี่ยวชาญของฉัน แต่ความจริงที่ว่าการมอบหมายด้านขวาก่อนหน้านี้เกิดขึ้นหลังจากการเพิ่มโพสต์อาจเป็นสิ่งที่สับสนที่นี่

นอกจากนี้คุณไม่คาดหวังว่าผลลัพธ์จะเป็น 2 ไม่ว่าคุณจะทำอะไร++iแทนที่จะi++เชื่อ


1
รุ่น preincrement สร้างผลลัพธ์2ด้วย C ++: ideone.com/8dH8tf
Steven Lu

นั่นทำให้รู้สึก แต่การเพิ่มล่วงหน้านั้นเป็นสถานการณ์ที่ซับซ้อนน้อยกว่าการเพิ่มขึ้นภายหลัง
gkimsey

2

เพียงแค่ใส่

i ++ จะเพิ่ม 1 ถึง "i" หลังจากโอเปอเรเตอร์ "+ =" เสร็จสิ้น

สิ่งที่คุณต้องการคือ ++ i ดังนั้นมันจะเพิ่ม 1 ถึง "i" ก่อนที่จะดำเนินการ "+ ="


0
i=0

i+=i

i=i+1

i=0;

จากนั้น 1 iจะถูกเพิ่ม

i + = ฉัน ++

ดังนั้นก่อนที่จะเพิ่ม 1 i, iเอาค่าเป็น 0 เท่านั้นถ้าเราเพิ่ม 1 ก่อนที่จะiได้รับค่า 0

i+=++i

i=2

-4

คำตอบคือiจะเป็น1จะเป็น

มาดูกันว่า:

ในขั้นต้น i=0;ในขั้นต้น

จากนั้นในขณะที่คำนวณi +=i++;ตามมูลค่าของเราจะมีลักษณะดังนี้0 +=0++;ดังนั้นตามลำดับความสำคัญของผู้0+=0ปฏิบัติงานจะดำเนินการก่อนและผลลัพธ์จะเป็น0จะดำเนินการเป็นครั้งแรกและผลจะเป็น

จากนั้นผู้ประกอบการที่เพิ่มขึ้นจะนำไปใช้เป็น0++เป็น0+1และความคุ้มค่าของการจะเป็นi1


3
คำตอบนี้ผิด คุณจะไม่ได้รับ 1 เพราะเมื่อคุณทำการ0 += 0++;มอบหมายหลังจากเพิ่มขึ้น++แต่ด้วยค่าของฉันตีความก่อนของ++(เพราะเป็นผู้ประกอบการโพสต์
PhoneixS

2
ขออภัย แต่นี่ไม่ถูกต้อง อ่านคำถามของฉันและคุณจะเห็นฉันบอกว่าผลลัพธ์คือ 0 ถ้าคุณเรียกใช้รหัสคุณจะเห็นว่ามันมีประสิทธิภาพ 0
Peter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.