พฤติกรรมที่ไม่ได้กำหนดและจุดลำดับ


986

"คะแนนตามลำดับ" คืออะไร?

ความสัมพันธ์ระหว่างพฤติกรรมที่ไม่ได้กำหนดและคะแนนตามลำดับคืออะไร?

ฉันมักจะใช้การแสดงออกที่ตลกและซับซ้อนเช่นa[++i] = i;ทำให้ฉันรู้สึกดีขึ้น เหตุใดฉันจึงควรหยุดใช้งาน

หากคุณได้อ่านบทความนี้ให้แน่ใจว่าจะแวะไปที่ติดตามคำถามไม่ได้กำหนดพฤติกรรมและลำดับจุดโหลดใหม่

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

คำตอบ:


682

C ++ 98 และ C ++ 03

คำตอบนี้สำหรับรุ่น C ++ ที่เก่ากว่า มาตรฐานรุ่น C ++ 11 และ C ++ 14 ไม่มีรูปแบบ 'จุดลำดับ' อย่างเป็นทางการ การดำเนินงานเป็น 'ลำดับก่อนหน้า' หรือ 'ไม่มาก่อน' หรือ 'ลำดับไม่สิ้นสุด' แทน ผลกระทบสุทธิเป็นหลักเหมือนกัน แต่คำศัพท์ที่แตกต่างกัน


ข้อจำกัดความรับผิดชอบ : โอเค คำตอบนี้ค่อนข้างยาว ดังนั้นจงอดทนขณะอ่านมัน หากคุณรู้สิ่งเหล่านี้แล้วการอ่านอีกครั้งจะไม่ทำให้คุณบ้า

สิ่งที่ต้องมีก่อน : ความรู้เบื้องต้นของC ++ มาตรฐาน


คะแนนลำดับคืออะไร

มาตรฐานพูดว่า

ในบางจุดที่ระบุไว้ในลำดับการดำเนินการที่เรียกว่าจุดลำดับผลข้างเคียงทั้งหมดของการประเมินก่อนหน้านี้จะต้องสมบูรณ์และจะไม่มีผลข้างเคียงของการประเมินครั้งต่อไปที่จะเกิดขึ้น (§1.9 / 7)

ผลข้างเคียง? ผลข้างเคียงคืออะไร?

การประเมินผลของการแสดงออกสร้างบางสิ่งบางอย่างและหากนอกจากนี้ยังมีการเปลี่ยนแปลงในสภาพแวดล้อมของการดำเนินการว่ากันว่าการแสดงออก (การประเมินผล) มีผลข้างเคียงบางอย่าง

ตัวอย่างเช่น:

int x = y++; //where y is also an int

นอกเหนือจากการดำเนินการเริ่มต้นค่าที่yได้รับการเปลี่ยนแปลงเนื่องจากผลข้างเคียงของ++ผู้ประกอบการ

จนถึงตอนนี้ดีมาก ย้ายไปยังจุดลำดับ นิยามการสลับของ seq-points ที่กำหนดโดยผู้เขียน comp.lang.c Steve Summit:

Sequence point เป็นจุดที่ฝุ่นตกลงมาและผลข้างเคียงทั้งหมดที่ได้รับการยืนยันจนถึงตอนนี้จะเสร็จสมบูรณ์


จุดลำดับทั่วไปที่แสดงอยู่ในมาตรฐาน C ++ คืออะไร

นั่นคือ:

  • ในตอนท้ายของการประเมินผลการแสดงออกเต็มรูปแบบ ( §1.9/16) (การแสดงออกเต็มรูปแบบคือการแสดงออกที่ไม่ subexpression ของการแสดงออกอื่น) 1

    ตัวอย่าง:

    int a = 5; // ; is a sequence point here
  • ในการประเมินผลของแต่ละนิพจน์ต่อไปนี้หลังจากการประเมินผลของนิพจน์แรก ( §1.9/18) 2

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18)(ที่นี่ a, b เป็นตัวดำเนินการด้วยเครื่องหมายจุลภาค; ในfunc(a,a++) ,ไม่ใช่ตัวดำเนินการด้วยเครื่องหมายจุลภาคมันเป็นเพียงตัวคั่นระหว่างอาร์กิวเมนต์aและa++ดังนั้นพฤติกรรมจะไม่ได้กำหนดไว้ในกรณีนั้น (หากaถือว่าเป็นประเภทดั้งเดิม))
  • ที่การเรียกใช้ฟังก์ชั่น (ไม่ว่าฟังก์ชั่นจะเป็นแบบอินไลน์) หรือไม่หลังจากการประเมินฟังก์ชั่นการโต้แย้งทั้งหมด (ถ้ามี) ที่เกิดขึ้นก่อนการดำเนินการของการแสดงออกหรือคำสั่งใด ๆ ในฟังก์ชั่นร่างกาย ( §1.9/17)

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

2: ตัวดำเนินการที่ระบุเป็นตัวดำเนินการในตัวตามที่อธิบายไว้ในข้อ 5 เมื่อหนึ่งในตัวดำเนินการเหล่านี้ถูกโหลดมากเกินไป (ข้อ 13) ในบริบทที่ถูกต้องดังนั้นการกำหนดฟังก์ชันตัวดำเนินการที่ผู้ใช้กำหนด ตัวถูกดำเนินการในรายการอาร์กิวเมนต์โดยไม่ต้องมีจุดลำดับโดยนัยระหว่างพวกเขา


พฤติกรรมที่ไม่ได้กำหนดคืออะไร

มาตรฐานกำหนดพฤติกรรมที่ไม่ได้กำหนดในหมวด§1.3.12เป็น

พฤติกรรมเช่นอาจเกิดขึ้นเมื่อใช้สร้างโปรแกรมที่ผิดพลาดหรือข้อมูลที่ผิดพลาดซึ่งมาตรฐานนี้มีการเรียกเก็บไม่มีความต้องการ 3

พฤติกรรมที่ไม่ได้กำหนดอาจถูกคาดหวังเมื่อมาตรฐานสากลนี้ละเว้นคำอธิบายของคำจำกัดความที่ชัดเจนของพฤติกรรม

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

ในระยะสั้นพฤติกรรมที่ไม่ได้กำหนดหมายถึงสิ่งที่สามารถเกิดขึ้นได้จาก daemons ที่บินออกจากจมูกของคุณไปยังแฟนของคุณกำลังตั้งครรภ์


ความสัมพันธ์ระหว่างพฤติกรรมที่ไม่ได้กำหนดและคะแนนต่อเนื่องคืออะไร?

ก่อนที่ผมจะได้รับเป็นที่คุณต้องทราบความแตกต่าง (s) ระหว่างพฤติกรรมที่ไม่ได้กำหนดพฤติกรรมและการดำเนินงานยังไม่ระบุกำหนดพฤติกรรม

the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecifiedนอกจากนี้คุณยังจะต้องรู้ว่า

ตัวอย่างเช่น:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

อีกตัวอย่างหนึ่งที่นี่


ตอนนี้มาตรฐานใน§5/4พูดว่า

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

มันหมายความว่าอะไร?

หมายความว่าอย่างไม่เป็นทางการระหว่างสองจุดลำดับต้องไม่แก้ไขตัวแปรมากกว่าหนึ่งครั้ง ในคำสั่งการแสดงออกnext sequence pointโดยปกติจะอยู่ที่เครื่องหมายอัฒภาคที่สิ้นสุดและที่previous sequence pointอยู่ในตอนท้ายของคำสั่งก่อนหน้า sequence pointsการแสดงออกอาจมีกลาง

จากประโยคข้างต้นนิพจน์ต่อไปนี้จะเรียกใช้พฤติกรรมที่ไม่ได้กำหนด:

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i;     // UB, same as above
++i = 2;     // UB, same as above
i = ++i + 1; // UB, same as above
++++++i;     // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

แต่การแสดงออกต่อไปนี้เป็นเรื่องปกติ:

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i);   // well defined 
int j = i;
j = (++i, i++, j*i); // well defined

  • 2) นอกจากนี้ค่าก่อนหน้านี้จะสามารถเข้าถึงได้เพียงเพื่อกำหนดค่าที่จะเก็บไว้

มันหมายความว่าอะไร? มันหมายความว่าถ้าวัตถุถูกเขียนไปยังที่อยู่ในการแสดงออกอย่างเต็มรูปแบบใด ๆ และเข้าถึงได้ภายในแสดงออกเดียวกันจะต้องมีส่วนเกี่ยวข้องโดยตรงในการคำนวณค่าที่จะเขียน

ตัวอย่างเช่นในi = i + 1การเข้าถึงทั้งหมดของi(ใน LHS และใน RHS) มีส่วนเกี่ยวข้องโดยตรงในการคำนวณค่าที่จะเขียน ดังนั้นจึงเป็นเรื่องปกติ

กฎนี้ จำกัด การแสดงออกทางกฎหมายอย่างมีประสิทธิภาพต่อผู้ที่เข้าถึงได้ก่อนหน้าการแก้ไข

ตัวอย่างที่ 1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

ตัวอย่างที่ 2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

ไม่ได้รับอนุญาตเพราะหนึ่งในการเข้าถึงของi(หนึ่งในa[i]) ไม่มีอะไรจะทำอย่างไรกับค่าที่จบลงด้วยการถูกเก็บไว้ใน i (ซึ่งเกิดขึ้นในi++) และดังนั้นจึงไม่มีวิธีที่ดีที่จะกำหนด - สำหรับความเข้าใจของเราหรือ คอมไพเลอร์ - ไม่ว่าการเข้าถึงควรเกิดขึ้นก่อนหรือหลังการเก็บค่าที่เพิ่มขึ้น ดังนั้นพฤติกรรมจึงไม่ได้กำหนด

ตัวอย่างที่ 3:

int x = i + i++ ;// Similar to above

ติดตามคำตอบสำหรับ C ++ 11 ที่นี่


45
*p++ = 4 ไม่ใช่พฤติกรรมที่ไม่ได้กำหนด ถูกตีความว่าเป็น*p++ ส่งคืน(สำเนา) และค่าที่เก็บไว้ในที่อยู่ก่อนหน้า ทำไมถึงเรียก UB มันเป็นเรื่องปกติอย่างสมบูรณ์แบบ *(p++)p++p
Prasoon Saurav

6
@ ไมค์: AFAIK ไม่มีสำเนา (มาตรฐาน) ของมาตรฐาน C ++ ที่คุณสามารถเชื่อมโยงได้
sbi

11
ถ้าอย่างนั้นคุณอาจมีลิงค์ไปยังหน้าคำสั่งซื้อที่เกี่ยวข้องของ ISO อย่างไรก็ตามความคิดเกี่ยวกับมันวลี "ความรู้เบื้องต้นของมาตรฐาน C ++" ดูเหมือนจะขัดแย้งกันเล็กน้อยในแง่เนื่องจากถ้าคุณอ่านมาตรฐานคุณจะผ่านระดับประถมศึกษา บางทีเราสามารถเขียนรายการสิ่งต่าง ๆ ในภาษาที่คุณต้องการความเข้าใจพื้นฐานเช่นไวยากรณ์ของนิพจน์ลำดับของการดำเนินการและตัวดำเนินการโอเวอร์โหลด
Mike DeSimone

41
ฉันไม่แน่ใจว่าการอ้างอิงมาตรฐานเป็นวิธีที่ดีที่สุดในการสอนมือใหม่
Inverse

6
@Adrian นิพจน์แรกเรียกใช้ UB เนื่องจากไม่มีจุดลำดับระหว่างตัวสุดท้าย++iกับตัวiกำหนด การแสดงออกที่สองไม่วิงวอน UB เพราะการแสดงออกไม่ได้เปลี่ยนค่าของi iในตัวอย่างที่สองi++นั้นตามด้วยจุดลำดับ ( ,) ก่อนที่จะมีการเรียกตัวดำเนินการกำหนด
Kolyunya

276

นี่คือการติดตามคำตอบก่อนหน้าของฉันและมีเนื้อหาที่เกี่ยวข้องกับ C ++ 11 .


วิชาบังคับก่อน : ความรู้เบื้องต้นของความสัมพันธ์ (คณิตศาสตร์)


จริงหรือไม่ที่ไม่มีคะแนนตามลำดับใน C ++ 11

ใช่ นี่เป็นเรื่องจริง

คะแนนลำดับได้ถูกแทนที่ด้วยติดใจก่อนและติดใจหลังจาก (และUnsequencedและIndeterminately ติดใจ ) ความสัมพันธ์ใน C ++ 11


อะไรคือ 'ลำดับก่อนหน้า' สิ่งนี้คืออะไร

Sequenced Before (§1.9 / 13)เป็นความสัมพันธ์ที่:

ระหว่างการประเมินผลที่ดำเนินการโดยเธรดเดี่ยวและสั่งบางส่วนที่เข้มงวด1

อย่างเป็นทางการมันหมายถึงการได้รับการประเมินผลที่สอง(ดูด้านล่าง) AและBถ้าAเป็นลำดับขั้นตอนก่อน Bแล้วการดำเนินการA จะนำหน้าBการดำเนินการของ ถ้าAไม่ได้ติดใจก่อนBและBไม่ได้ติดใจก่อนAแล้วAและBมีunsequenced 2

การประเมินผลAและBจะติดใจ indeterminatelyเมื่อทั้งAเป็นลำดับขั้นตอนก่อนBหรือBเป็นลำดับขั้นตอนก่อนAแต่มันก็ไม่เกี่ยวกับบุคคลที่3

[หมายเหตุ]
ที่ 1: ส่วนคำสั่งที่เข้มงวดเป็นฐานความสัมพันธ์ "<"มากกว่าชุดPซึ่งเป็นasymmetricและtransitiveนั่นคือทั้งหมดa, bและcในPเรามีที่:
........ (i) ถ้า a <b ดังนั้น¬ (b <a) ( asymmetry);
........ (ii) ถ้า a <b และ b <c ดังนั้น a <c ( transitivity)
2: การดำเนินการของการประเมินผล unsequencedสามารถทับซ้อน
3: การประเมินตามลำดับที่กำหนดไม่สามารถซ้อนทับกันแต่สามารถดำเนินการก่อนได้


ความหมายของคำว่า 'การประเมินผล' ในบริบทของ C ++ 11 คืออะไร

ใน C ++ 11 การประเมินผลของนิพจน์ (หรือนิพจน์ย่อย) โดยทั่วไปรวมถึง:

  • การคำนวณมูลค่า (รวมถึงการกำหนดตัวตนของวัตถุสำหรับการประเมินค่า glvalueและดึงค่าที่กำหนดไว้ก่อนหน้านี้ให้กับวัตถุสำหรับการประเมินค่า prvalue ) และ

  • การเริ่มต้นของผลข้างเคียง

ตอนนี้ (§1.9 / 14) พูดว่า:

ทุกการคำนวณมูลค่าและผลข้างเคียงที่เกี่ยวข้องกับการเต็มรูปแบบการแสดงออกเป็นลำดับขั้นตอนก่อนที่ทุกการคำนวณมูลค่าและผลข้างเคียงที่เกี่ยวข้องกับการเต็มรูปแบบการแสดงออกต่อไปที่จะประเมิน

  • ตัวอย่างเล็กน้อย:

    int x; x = 10; ++x;

    การคำนวณมูลค่าและผลข้างเคียงที่เกี่ยวข้องกับ++xจะถูกจัดลำดับหลังจากการคำนวณมูลค่าและผลข้างเคียงของx = 10;


ดังนั้นจะต้องมีความสัมพันธ์ระหว่างพฤติกรรมที่ไม่ได้กำหนดและสิ่งที่กล่าวถึงข้างต้นใช่มั้ย

ใช่ ขวา.

ใน (§1.9 / 15) มันได้รับการกล่าวถึงว่า

ยกเว้นที่ระบุไว้, การประเมินผลของตัวถูกดำเนินการของผู้ประกอบการแต่ละบุคคลและของ subexpressions ของการแสดงออกของแต่ละบุคคลมีunsequenced 4

ตัวอย่างเช่น :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. การประเมินตัวถูกดำเนินการของตัวดำเนิน+การจะไม่สัมพันธ์กัน
  2. การประเมินผลของตัวถูกดำเนินการ<<และ>>ตัวดำเนินการจะไม่สัมพันธ์กัน

4: ในการแสดงออกที่ที่ได้รับการประเมินมากกว่าหนึ่งครั้งในระหว่างการดำเนินการของโปรแกรมการunsequencedและติดใจ indeterminatelyการประเมินผลของ subexpressions มันไม่จำเป็นต้องดำเนินการอย่างต่อเนื่องในการประเมินผลที่แตกต่างกัน

(§1.9 / 15) การคำนวณค่าของตัวถูกดำเนินการของผู้ประกอบการจะถูกจัดลำดับก่อนการคำนวณมูลค่าของผลลัพธ์ของผู้ประกอบการ

นั่นหมายความว่าในx + yการคำนวณค่าของxและจะติดใจก่อนการคำนวณมูลค่าของy(x + y)

ที่สำคัญกว่า

(§1.9 / 15) หากผลข้างเคียงของวัตถุสเกลาร์นั้นไม่สัมพันธ์กัน

(a) ผลข้างเคียงอื่นบนวัตถุสเกลาร์เดียวกัน

หรือ

(b) การคำนวณค่าโดยใช้ค่าของวัตถุสเกลาร์เดียวกัน

พฤติกรรมจะไม่ได้กำหนด

ตัวอย่าง:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

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

การแสดงออก(5), (7)และ(8)ไม่เรียกไม่ได้กำหนดพฤติกรรม ตรวจสอบคำตอบต่อไปนี้สำหรับคำอธิบายโดยละเอียดเพิ่มเติม


หมายเหตุสุดท้าย :

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


3
แทนที่จะเป็น "ไม่สมมาตร" เรียงลำดับก่อน / หลังคือความสัมพันธ์ "antisymmetric" สิ่งนี้ควรเปลี่ยนในข้อความเพื่อให้สอดคล้องกับคำนิยามของคำสั่งบางส่วนที่กำหนดในภายหลัง (ซึ่งเห็นด้วยกับ Wikipedia)
TemplateRex

1
ทำไม 7) รายการในตัวอย่างสุดท้ายคือ UB บางทีมันควรจะเป็นf(i = -1, i = 1)?
Mikhail

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

1
5) การทำตัวให้ดีทำให้จิตใจของฉันออกไป คำอธิบายของ Johannes Schaub ไม่ได้ตรงไปตรงมาเลย โดยเฉพาะอย่างยิ่งเพราะฉันเชื่อว่าแม้ใน++i(เป็นค่าประเมินก่อนที่+ผู้ประกอบการที่ใช้มัน) มาตรฐานยังไม่ได้บอกว่าผลข้างเคียงของมันจะต้องเสร็จสิ้น แต่ในความเป็นจริงเพราะจะส่งกลับไปเตะlvalueซึ่งเป็นiตัวเองก็จะต้องมีการดำเนินการเสร็จสิ้นผลข้างเคียงตั้งแต่การประเมินผลจะต้องมีการดำเนินการเสร็จสิ้นจึงคุ้มค่าจะต้องถึงวันที่ นี่คือส่วนที่บ้าคลั่งที่จะได้รับจริง
v.oddou

"สมาชิกคณะกรรมการ ISO C ++ คิดว่าสิ่งต่าง ๆ ตามลำดับคะแนนนั้นค่อนข้างยากที่จะเข้าใจดังนั้นพวกเขาจึงตัดสินใจแทนที่ด้วยความสัมพันธ์ดังกล่าวข้างต้นเพื่อให้ได้ถ้อยคำที่ชัดเจนยิ่งขึ้นและเพิ่มความแม่นยำ" - คุณได้รับการอ้างอิงสำหรับการอ้างสิทธิ์นั้นหรือไม่? สำหรับฉันแล้วความสัมพันธ์ใหม่นั้นยากที่จะเข้าใจ
MM

30

C ++ 17 ( N4659) รวมถึงข้อเสนอการปรับแต่งคำสั่งการประเมินผลนิพจน์สำหรับ Idiomatic C ++ ซึ่งกำหนดลำดับที่เข้มงวดของการประเมินผลการแสดงออก

โดยเฉพาะประโยคต่อไปนี้

8.18 การมอบหมายและผู้ประกอบการมอบหมายสารประกอบ :
....

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

พร้อมคำชี้แจงดังต่อไปนี้

การแสดงออกXกล่าวจะติดใจก่อนการแสดงออกYถ้าทุกการคำนวณมูลค่าและผลข้างเคียงทุกที่เกี่ยวข้องกับการแสดงออกXจะติดใจทุกครั้งก่อนการคำนวณมูลค่าและผลข้างเคียงทุกที่เกี่ยวข้องกับการแสดงออกY

ทำให้หลายกรณีที่พฤติกรรมที่ไม่ได้กำหนดไว้ก่อนหน้านี้ใช้งานได้รวมถึงกรณีที่มีปัญหา:

a[++i] = i;

อย่างไรก็ตามยังมีอีกหลายกรณีที่คล้ายกันที่นำไปสู่พฤติกรรมที่ไม่ได้กำหนด

ในN4140:

i = i++ + 1; // the behavior is undefined

แต่ค่ะ N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

แน่นอนว่าการใช้คอมไพเลอร์ที่เข้ากันได้กับ C ++ 17 ไม่ได้หมายความว่าควรเริ่มเขียนนิพจน์ดังกล่าว


เหตุผลที่i = i++ + 1;มีการกำหนดพฤติกรรมใน c ++ 17 ผมคิดว่าแม้ว่า "ตัวถูกดำเนินการที่เหมาะสมเป็นลำดับขั้นตอนก่อนที่จะถูกดำเนินการทางด้านซ้าย" แต่การปรับเปลี่ยนสำหรับ "i ++" และผลข้างเคียงสำหรับการกำหนดกำลัง unsequenced โปรดให้รายละเอียดเพิ่มเติมในการตีความเหล่านี้
jack X

@jackX ฉันได้ขยายคำตอบ :)
AlexD

ใช่ฉันคิดว่ารายละเอียดของการตีความประโยค "ตัวถูกดำเนินการถูกจัดลำดับไว้ก่อนตัวถูกดำเนินการด้านซ้าย" มีประโยชน์มากกว่าเช่น "ตัวถูกดำเนินการถูกจัดลำดับก่อนตัวถูกดำเนินการด้านซ้าย" หมายถึงการคำนวณมูลค่าและผลข้างเคียง เรียงลำดับก่อนหน้าของตัวถูกดำเนินการด้านซ้าย ตามที่คุณทำ :-)
jack X

11

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

f (a,b)

ก่อนหน้านี้ทั้ง a แล้ว b หรือ b จากนั้น ตอนนี้สามารถประเมิน a และ b ด้วยคำสั่ง interleaved หรือแม้แต่ในแกนที่แตกต่างกัน


5
ฉันเชื่อว่าถ้า 'a' หรือ 'b' รวมถึงการเรียกใช้ฟังก์ชันพวกเขาจะถูกจัดลำดับแบบไม่กำหนดเวลามากกว่าที่จะตามมาซึ่งจะบอกว่าผลข้างเคียงทั้งหมดจากคนหนึ่งจะต้องเกิดขึ้นก่อนผลข้างเคียงใด ๆ จาก อื่น ๆ แม้ว่าคอมไพเลอร์ไม่จำเป็นต้องสอดคล้องกับที่หนึ่งไปก่อน หากไม่เป็นจริงอีกต่อไปมันจะทำลายรหัสจำนวนมากซึ่งขึ้นอยู่กับการดำเนินการที่ไม่ทับซ้อนกัน (เช่นถ้า 'a' และ 'b' แต่ละการตั้งค่าใช้งานและล้มลงสถานะคงที่ที่ใช้ร่วมกัน)
supercat

2

ในC99(ISO/IEC 9899:TC3)ซึ่งดูเหมือนว่าขาดจากการสนทนานี้ป่านนี้ steteents ต่อไปนี้จะทำเกี่ยวกับคำสั่งของ evaluaiton

[... ] ลำดับของการประเมินผลของนิพจน์ย่อยและลำดับของผลข้างเคียงที่เกิดขึ้นทั้งที่ไม่ระบุ (มาตรา 6.5 หน้า 67)

ลำดับการประเมินผลของตัวถูกดำเนินการไม่ได้ระบุ หากมีความพยายามในการปรับเปลี่ยนผลลัพธ์ของผู้ดำเนินการที่ได้รับมอบหมายหรือเข้าถึงได้หลังจากจุดลำดับถัดไปพฤติกรรม [sic] จะไม่ได้กำหนด (มาตรา 6.5.16 หน้า 91)


2
คำถามถูกติดแท็ก C ++ ไม่ใช่ C ซึ่งดีเพราะพฤติกรรมใน C ++ 17 ค่อนข้างแตกต่างจากพฤติกรรมในรุ่นเก่า - และไม่มีความสัมพันธ์กับพฤติกรรมใน C11, C99, C90 ฯลฯ หรือมีน้อยมาก เกี่ยวข้องกับมัน โดยรวมแล้วฉันขอแนะนำให้นำสิ่งนี้ออก ที่สำคัญยิ่งกว่านั้นเราต้องค้นหาคำถามและคำตอบที่เทียบเท่าสำหรับ C และตรวจสอบให้แน่ใจว่ามันใช้ได้ (และโปรดสังเกตว่าโดยเฉพาะอย่างยิ่ง C ++ 17 เปลี่ยนกฎ - พฤติกรรมใน C ++ 11 และก่อนหน้านั้นไม่เหมือนกับหรือมากกว่านั้น ใน C11 แม้ว่าคำฟุ่มเฟือยที่อธิบายไว้ใน C ยังคงใช้ 'จุดลำดับ' ในขณะที่ C ++ 11 และใหม่กว่าไม่ได้
Jonathan Leffler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.