นี่คือ UB; ในเงื่อนไข ISO C ++ พฤติกรรมทั้งหมดของโปรแกรมทั้งหมดนั้นไม่ได้ระบุอย่างสมบูรณ์สำหรับการดำเนินการที่ในที่สุดก็กระทบ UB ตัวอย่างคลาสสิกนั้นไกลถึงมาตรฐาน C ++ มันสามารถทำให้ปีศาจบินออกจากจมูกของคุณได้ (ฉันแนะนำไม่ให้ใช้การนำไปปฏิบัติในกรณีที่ปีศาจในจมูกเป็นไปได้จริง) ดูคำตอบอื่น ๆ สำหรับรายละเอียดเพิ่มเติม
คอมไพเลอร์สามารถ "ทำให้เกิดปัญหา" ณ เวลารวบรวมสำหรับเส้นทางของการดำเนินการที่พวกเขาสามารถเห็นได้ว่านำไปสู่การรวบรวม UB ที่มองเห็นได้ในเวลารวบรวมเช่นสมมติว่าบล็อกพื้นฐานเหล่านั้นจะไม่ถึง
ดูสิ่งที่โปรแกรมเมอร์ C ทุกคนควรรู้เกี่ยวกับพฤติกรรมที่ไม่ได้กำหนด (บล็อก LLVM) ตามที่ได้อธิบายมีการลงนามล้น UB ช่วยให้คอมไพเลอร์จะพิสูจน์ว่าลูปลูปไม่ได้ไม่มีที่สิ้นสุดแม้ไม่รู้จักfor(... i <= n ...)
n
นอกจากนี้ยังช่วยให้พวกเขา "ส่งเสริม" เคาน์เตอร์วนรอบ int ถึงความกว้างของตัวชี้แทนการทำซ้ำส่วนขยายสัญญาณ (ดังนั้นผลที่ตามมาของ UB ในกรณีนั้นอาจเป็นการเข้าถึงภายนอกองค์ประกอบ 64k หรือ 4G ที่ต่ำของอาเรย์ถ้าคุณคาดว่าจะมีการตัดคำของi
ในช่วงค่าของมัน)
ในบางกรณีคอมไพเลอร์จะส่งคำสั่งที่ผิดกฎหมายเช่น x86 ud2
สำหรับบล็อกที่พิสูจน์ได้ว่าเป็นสาเหตุของ UB หากเคยถูกประหารชีวิต (โปรดทราบว่าอาจไม่มีการเรียกใช้ฟังก์ชั่นดังนั้นคอมไพเลอร์จึงไม่สามารถใช้งานได้โดยทั่วไปและทำลายฟังก์ชั่นอื่น ๆ หรือแม้แต่เส้นทางที่เป็นไปได้ผ่านฟังก์ชั่นที่ไม่ได้กด UB เช่นรหัสเครื่องจักรที่คอมไพล์ อินพุตทั้งหมดที่ไม่นำไปสู่ UB)
อาจเป็นวิธีที่มีประสิทธิภาพมากที่สุดคือการลอกแบบวนซ้ำล่าสุดด้วยตนเองเพื่อfactor*=10
หลีกเลี่ยงการใช้ที่ไม่จำเป็น
int result = 0;
int factor = 1;
for (... i < n-1) { // stop 1 iteration early
result = ...
factor *= 10;
}
result = ... // another copy of the loop body, using the last factor
// factor *= 10; // and optimize away this dead operation.
return result;
หรือถ้าร่างกายของวงที่มีขนาดใหญ่พิจารณาเพียงแค่ใช้ชนิดที่ได้รับการรับรองสำหรับ factor
จากนั้นคุณสามารถปล่อยให้การคูณทวีคูณที่ไม่ได้ลงนามและมันจะทำการตัดที่กำหนดไว้อย่างดีกับกำลังของ 2 (จำนวนของค่าบิตในประเภทที่ไม่ได้ลงนาม)
สิ่งนี้จะใช้ได้แม้ว่าคุณจะใช้กับประเภทที่เซ็นชื่อแล้วโดยเฉพาะอย่างยิ่งหากการแปลงที่ไม่ได้ลงนาม -> ที่ลงนามแล้วจะไม่ล้น
การแปลงระหว่างส่วนเสริมที่ไม่ได้ลงนามและส่วนเสริม 2 นั้นฟรี (รูปแบบบิตเดียวกันสำหรับค่าทั้งหมด); การห่อโมดูโลสำหรับ int -> ที่ไม่ได้ลงนามซึ่งระบุโดยมาตรฐาน C ++ ช่วยให้ง่ายขึ้นเพียงแค่ใช้รูปแบบบิตเดียวกันซึ่งต่างจากส่วนประกอบหรือเครื่องหมาย / ขนาด
และ unsigned-> INT_MAX
ลงนามเป็นที่น่ารำคาญในทำนองเดียวกันแม้ว่ามันคือการดำเนินการกำหนดค่าที่มีขนาดใหญ่กว่า หากคุณไม่ได้ใช้ผลลัพธ์ที่ไม่ได้ลงชื่อจำนวนมากจากการทำซ้ำครั้งล่าสุดคุณไม่มีอะไรต้องกังวล แต่ถ้าคุณเป็นโปรดดูการแปลงจากไม่ได้รับการลงนามเป็นไม่ได้ลงนามหรือไม่? . ตัวพิมพ์เล็ก - ใหญ่ไม่เหมาะสมกับการใช้งานซึ่งกำหนดไว้ซึ่งหมายความว่าการใช้งานต้องเลือกพฤติกรรมบางอย่าง คนที่มีสติเพียงแค่ตัดทอน (ถ้าจำเป็น) รูปแบบบิตที่ไม่ได้ลงชื่อและใช้เป็นรูปแบบลายเซ็นเพราะใช้กับค่าที่อยู่ในช่วงแบบเดียวกับที่ไม่มีงานพิเศษ และแน่นอนว่าไม่ใช่ UB ดังนั้นค่าที่ไม่ได้ลงนามขนาดใหญ่สามารถกลายเป็นจำนวนเต็มลบได้ เช่นหลังจากint x = u;
gcc และ clang อย่าปรับให้เหมาะสมx>=0
เป็นความจริงเสมอแม้ไม่มี-fwrapv
เพราะพวกเขากำหนดพฤติกรรม