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