ผลลัพธ์ของ + = ใน C และ C ++ คืออะไร?


93

ฉันมีรหัสต่อไปนี้:

#include <stdio.h>
int main(int argc, char **argv) {
    int i = 0;
    (i+=10)+=10;
    printf("i = %d\n", i);
    return 0;
}

หากฉันพยายามรวบรวมเป็นแหล่ง C โดยใช้ gcc ฉันได้รับข้อผิดพลาด:

error: lvalue required as left operand of assignment

แต่ถ้าฉันรวบรวมเป็นซอร์ส C ++ โดยใช้ g ++ ฉันไม่ได้รับข้อผิดพลาดและเมื่อฉันเรียกใช้ไฟล์ปฏิบัติการ:

i = 20

ทำไมพฤติกรรมต่างกัน?


85
ภาษาที่แตกต่างกันกฎไวยากรณ์ที่แตกต่างกัน? โดยส่วนตัวฉันจะปฏิเสธรหัสนั้นในการตรวจสอบโค้ด
สูงสุด

7
หลีกเลี่ยงรหัสเช่น imo นี้ ... ไม่ชัดเจนสำหรับทุกคน
allaire

1
ไม่ต้องสงสัยเลยว่าโค้ดไม่สะอาดและควรหลีกเลี่ยงในการพัฒนา "ของจริง" แต่อย่างไรก็ตามฉันสังเกตเห็นพฤติกรรมเดียวกันนี้และต้องการทราบเหตุผลของมัน
ulidtko

9
นี่ไม่ใช่โค้ดที่ตัดตอนมาจากซอฟต์แวร์จริง นี่เป็นเพียงความหงิกงอที่ฉันสะดุดโดยบังเอิญ
Svetlin Mladenov

3
@JohnDibling ฉันคิดว่าการโหวตเพิ่มขึ้นโดยเฉพาะ (i + = 10) + = 10 ฉันไม่รู้ว่าภาษาใดที่เป็นรหัสที่ถูกต้องและความจริงที่ว่าเขากล่าวว่า C ++ รวบรวมมันทำให้ฉันสนใจ
Tony318

คำตอบ:


133

ความหมายของตัวดำเนินการกำหนดสารประกอบแตกต่างกันใน C และ C ++:

มาตรฐาน C99 6.5.16 ตอนที่ 3:

ตัวดำเนินการกำหนดจะเก็บค่าในอ็อบเจ็กต์ที่กำหนดโดยตัวถูกดำเนินการด้านซ้าย นิพจน์การกำหนดมีค่าของตัวถูกดำเนินการด้านซ้ายหลังการกำหนด แต่ไม่ใช่ค่า lvalue

ใน C ++ 5.17.1:

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

แก้ไข:พฤติกรรมของ(i+=10)+=10C ++ ไม่ได้กำหนดไว้ใน C ++ 98 แต่กำหนดไว้อย่างดีใน C ++ 11 ดูคำตอบสำหรับคำถามนี้โดยNPEสำหรับส่วนที่เกี่ยวข้องของมาตรฐาน


แก้ไข. หนึ่งคืนค่าผลลัพธ์และหนึ่งส่งกลับตัวแปร (ที่อยู่)
texasbruce

7
สำคัญ : โปรดทราบว่า(i+=10)+=10เป็นพฤติกรรมที่ไม่ได้กำหนดใน C ++ โปรดดูคำตอบ @aix
David Rodríguez - น้ำลายไหล

@ DavidRodríguez-dribeas คุณหมายถึงไม่ระบุไม่ระบุใช่มั้ย?
Sergey Kalinichenko

4
@dasblinkenlight: ไม่มีเขาหมายถึงไม่ได้กำหนด ใน C ++ 03 และก่อนหน้านี้การแก้ไขผลลัพธ์ lvalue ของนิพจน์จะทำงานอย่างไม่สามารถคาดเดาได้ในคอมไพเลอร์ทั้งหมดเนื่องจากไม่มีจุดลำดับการแทรกแซง ถ้ามันไม่เกี่ยวกับบุคคลก็จะมีพฤติกรรมที่ไม่คาดฝัน แต่แตกต่างกันในคอมไพเลอร์ที่แตกต่างกัน
จัสตินᚅᚔᚈᚄᚒᚔ

2
มันจะมีประโยชน์ในการตั้งค่าเช่นint f(int &y); f(x += 10);- ส่งการอ้างอิงไปยังตัวแปรที่แก้ไขลงในฟังก์ชัน
Phil Miller

51

นอกจากจะเป็นรหัส C ที่ไม่ถูกต้องแล้วบรรทัด

(i+=10)+=10;

จะส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดทั้งใน C และ C ++ 03 เนื่องจากจะแก้ไข iสองครั้งระหว่างจุดลำดับ

สาเหตุที่อนุญาตให้คอมไพล์ใน C ++:

[C ++ N3242 5.17.1] ตัวดำเนินการกำหนด (=) และตัวดำเนินการกำหนดแบบผสมทั้งหมดจัดกลุ่มจากขวาไปซ้าย ทั้งหมดต้องการ lvalue ที่ปรับเปลี่ยนได้เป็นตัวถูกดำเนินการด้านซ้ายและส่งคืนค่า lvalue ที่อ้างถึงตัวถูกดำเนินการด้านซ้าย

ย่อหน้าเดียวกันกล่าวต่อไปว่า

ในทุกกรณีการกำหนดจะเรียงตามลำดับหลังจากการคำนวณค่าของตัวถูกดำเนินการด้านขวาและด้านซ้ายและก่อนการคำนวณค่าของนิพจน์การกำหนด

สิ่งนี้ชี้ให้เห็นว่าใน C ++ 11 นิพจน์ไม่มีพฤติกรรมที่ไม่ได้กำหนดอีกต่อไป


3
มันเป็น UB อย่างแน่นอนเพราะจุดลำดับ นอกจากนี้โค้ดที่ไม่ถูกต้องใน C (แต่ไม่ใช่ C ++) แต่ไม่เกี่ยวข้องกับจุดลำดับและคอมไพเลอร์ควรจับได้
Konrad Rudolph

2
@KonradRudolph: ไม่คอมไพเลอร์ไม่จำเป็นต้องตรวจจับพฤติกรรมที่ไม่ได้กำหนดซึ่งตรงข้ามกับรหัสที่ไม่ถูกต้อง ส่วน "ควรจับโดยคอมไพเลอร์" คือส่วนที่เราไม่เห็นด้วย

2
จุดลำดับไม่มีอยู่ใน C ++ 11 ดังนั้นเหตุผลที่แท้จริงสำหรับ UB คือมีการปรับเปลี่ยนสองอย่างiที่ไม่ได้เกิดขึ้น
Mankarse

4
เป็นเท็จว่านี่เป็นพฤติกรรมที่ไม่ได้กำหนด หากการกำหนดไม่เรียงลำดับก่อนการคำนวณค่าของนิพจน์การกำหนดi = j+=1จะทำให้ได้ค่าที่ไม่แน่นอน จากย่อหน้าเดียวกันที่คุณอ้างถึง "ในทุกกรณีการกำหนดจะเรียงตามลำดับหลังจากการคำนวณค่าของตัวถูกดำเนินการด้านขวาและด้านซ้ายและก่อนการคำนวณค่าของนิพจน์การกำหนด" ดังนั้นจึงมีการกำหนดไว้อย่างดีที่จะทำ(i+=10)+=10 i += 10; i += 10;ในทางกลับกัน(i+=10)+=(i+=10)คือ UB
bames53

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