ปรัชญาที่อยู่เบื้องหลังพฤติกรรมที่ไม่ได้กำหนด


59

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

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

ฉันอ่านโพสต์บล็อกที่ยอดเยี่ยมที่ระบุว่าประสิทธิภาพเป็นเหตุผลหลัก ฉันสงสัยว่าการทำงานเป็นเพียงเกณฑ์ในการอนุญาตหรือมีปัจจัยอื่น ๆ ที่มีอิทธิพลต่อการตัดสินใจที่จะเปิดโอกาสให้คอมไพเลอร์ใช้งานได้หรือไม่?

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

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


7
ใครบ้างที่เคยได้ยินเรื่องคอมพิวเตอร์เลวร้าย
sova

1
เป็น litb ของคำตอบที่ดีprogrammers.stackexchange.com/a/99741/192238ระบุชื่อเรื่องและเนื้อหาของคำถามนี้ดูเหมือนนิด ๆ หน่อย ๆ ไม่ตรงกัน "พฤติกรรมการเปิดให้บริการสำหรับคอมไพเลอร์ที่จะใช้ในทางของตัวเอง" มักจะเรียกว่าการดำเนินการตามที่กำหนดไว้ . แน่นอนว่า UB ที่แท้จริงนั้นได้รับอนุญาตให้ถูกกำหนดโดยผู้สร้างการใช้งาน แต่บ่อยครั้งที่พวกเขาไม่ได้ใส่ใจ (และปรับให้เหมาะสมทั้งหมดออกไป ฯลฯ )
underscore_d

คำตอบ:


49

ก่อนอื่นฉันจะทราบว่าถึงแม้ว่าฉันจะพูดถึง "C" เท่านั้นที่นี่สิ่งเดียวกันก็ใช้ได้กับ C ++ เช่นกัน

ความคิดเห็นที่กล่าวถึง Godel เป็นเพียงส่วนหนึ่ง (แต่เพียงบางส่วนเท่านั้น) ตรงประเด็น

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

ทฤษฎีบทของ Godel (มีอยู่สองข้อ) โดยทั่วไปบอกว่ามันเป็นไปไม่ได้ที่จะกำหนดระบบคณิตศาสตร์ที่สามารถพิสูจน์ได้ (ตามกฎของตัวเอง) ให้สมบูรณ์และสอดคล้องกัน คุณสามารถสร้างกฎของคุณเพื่อให้เสร็จสมบูรณ์ (กรณีที่เขาจัดการคือกฎ "ปกติ" สำหรับตัวเลขธรรมชาติ) หรือคุณสามารถทำให้เป็นไปได้ที่จะพิสูจน์ความมั่นคง แต่คุณไม่สามารถมีได้ทั้งคู่

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

ด้วยความเสี่ยงที่จะถูกกล่าวหาว่ามีความเย่อหยิ่ง (อีกครั้ง) ฉันจะอธิบายถึงมาตรฐาน C ว่าอยู่ภายใต้การควบคุม (บางส่วน) โดยแนวคิดพื้นฐานสองประการ:

  1. ภาษาควรรองรับฮาร์ดแวร์ที่หลากหลายเท่าที่จะเป็นไปได้ (โดยหลักแล้วฮาร์ดแวร์ทั้งหมด "มีเหตุผล" จนถึงขีด จำกัด ล่างที่สมเหตุสมผล)
  2. ภาษาควรรองรับการเขียนซอฟต์แวร์ที่หลากหลายที่สุดเท่าที่จะเป็นไปได้สำหรับสภาพแวดล้อมที่กำหนด

สิ่งแรกหมายความว่าถ้ามีคนกำหนดซีพียูใหม่มันควรจะเป็นไปได้ที่จะให้การใช้งาน C ที่ดีมั่นคงและใช้งานได้ตราบใดที่การออกแบบตกอยู่อย่างน้อยก็ใกล้เคียงกับแนวทางง่ายๆสองสามประการ ติดตามบางสิ่งตามคำสั่งทั่วไปของแบบจำลอง Von Neumann และให้หน่วยความจำขั้นต่ำที่เหมาะสมอย่างน้อยก็พอที่จะอนุญาตให้มีการติดตั้ง C สำหรับการนำไปใช้งาน "ที่โฮสต์" (ที่ทำงานบนระบบปฏิบัติการ) คุณต้องสนับสนุนความคิดบางอย่างที่สอดคล้องกับไฟล์อย่างใกล้ชิดและมีชุดอักขระที่มีชุดอักขระขั้นต่ำ (91 รายการ)

อย่างที่สองหมายความว่าควรจะเขียนโค้ดที่จัดการกับฮาร์ดแวร์โดยตรงดังนั้นคุณสามารถเขียนสิ่งต่าง ๆ เช่น boot loader, ระบบปฏิบัติการ, ซอฟต์แวร์ฝังตัวที่ทำงานโดยไม่มีระบบปฏิบัติการใด ๆ ฯลฯ ในที่สุดก็มีข้อ จำกัดบางประการในเรื่องนี้เกือบทั้งหมด ระบบปฏิบัติการ, บูตโหลดเดอร์และอื่น ๆ มีแนวโน้มที่จะมีโค้ดอย่างน้อยเขียนในภาษาแอสเซมบลี ในทำนองเดียวกันแม้แต่ระบบสมองกลฝังตัวที่มีขนาดเล็กก็น่าจะรวมรูทีนไลบรารีที่เขียนไว้ล่วงหน้าอย่างน้อยบางประเภทเพื่อให้สามารถเข้าถึงอุปกรณ์ในระบบโฮสต์ แม้ว่าขอบเขตที่แม่นยำนั้นยากที่จะกำหนด แต่ความตั้งใจก็คือการพึ่งพารหัสดังกล่าวควรถูกเก็บไว้ให้น้อยที่สุด

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

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

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

ความแตกต่างในเจตนาจะผลักดันความแตกต่างระหว่าง C และบางอย่างเช่น Java หรือระบบที่ใช้ CLI ของ Microsoft หลังค่อนข้าง จำกัด อย่างชัดเจนในการทำงานกับชุดฮาร์ดแวร์ที่ จำกัด มากขึ้นหรือต้องการซอฟต์แวร์เพื่อจำลองฮาร์ดแวร์เฉพาะที่พวกเขากำหนดเป้าหมาย พวกเขาตั้งใจที่จะป้องกันการจัดการฮาร์ดแวร์โดยตรงโดยเฉพาะแทนที่จะต้องการให้คุณใช้บางอย่างเช่น JNI หรือ P / Invoke (และรหัสที่เขียนในรูปแบบ C) เพื่อให้พยายามทำเช่นนั้น

กลับไปที่ทฤษฎีบทของ Godel สักครู่เราสามารถวาดบางสิ่งที่ขนานกัน: Java และ CLI เลือกใช้ทางเลือก "ที่สอดคล้องกันภายใน" ในขณะที่ C เลือกใช้ทางเลือก "สมบูรณ์" ของหลักสูตรนี้คือการเปรียบเทียบหยาบมาก - ฉันสงสัยของทุกคนพยายามพิสูจน์อย่างเป็นทางการของทั้งสอดคล้องภายในหรือความครบถ้วนสมบูรณ์ในทั้งสองกรณี อย่างไรก็ตามความคิดทั่วไปไม่พอดีกับที่ค่อนข้างใกล้ชิดกับตัวเลือกที่พวกเขาได้นำ


25
ฉันคิดว่าทฤษฎีบทของ Godel นั้นเป็นปลาเฮอริ่งแดง พวกเขาจัดการกับการพิสูจน์ระบบจากสัจพจน์ของตัวเองซึ่งไม่ใช่ในกรณีนี้: C ไม่จำเป็นต้องระบุใน C มันเป็นไปได้มากที่จะมีภาษาที่ระบุอย่างสมบูรณ์ (พิจารณาเครื่องทัวริง)
30651 Poolie

9
ขออภัยฉันกลัวว่าคุณเข้าใจทฤษฎีบทของ Godel ผิดไปโดยสิ้นเชิง พวกเขาจัดการกับความเป็นไปไม่ได้ที่จะพิสูจน์ข้อความจริงทั้งหมดในระบบตรรกะที่สอดคล้องกัน; ในแง่ของการคำนวณทฤษฎีบทความไม่สมบูรณ์นั้นคล้ายคลึงกับการบอกว่ามีปัญหาที่ไม่สามารถแก้ไขได้โดยโปรแกรมใด ๆ - ปัญหานั้นคล้ายคลึงกับข้อความจริงโปรแกรมเพื่อพิสูจน์และแบบจำลองการคำนวณกับระบบตรรกะ มันไม่มีการเชื่อมต่อกับพฤติกรรมที่ไม่ได้กำหนดเลย ดูคำอธิบายของการเปรียบเทียบที่นี่: scottaaronson.com/blog/?p=710
Alex ten Brink

5
ฉันควรทราบว่าไม่จำเป็นต้องใช้เครื่อง Von Neumann สำหรับการติดตั้ง C มันเป็นไปได้อย่างสมบูรณ์แบบ (และไม่ได้ยากมาก) ในการพัฒนาการดำเนินงาน C เป็นสถาปัตยกรรมที่ฮาร์วาร์ (และฉันจะไม่แปลกใจที่จะเห็นจำนวนมากของการใช้งานดังกล่าวในระบบฝังก)
bdonlan

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

20

C เหตุผลอธิบาย

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

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

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

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

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

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

และที่ 1.7 มันบันทึก

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

โปรแกรมที่สอดคล้องอย่างเคร่งครัดเป็นอีกคำสำหรับโปรแกรมพกพาสูงสุด เป้าหมายคือเพื่อให้โปรแกรมเมอร์มีโอกาสในการต่อสู้เพื่อสร้างโปรแกรม C ที่ทรงพลังซึ่งยังพกพาได้สูงโดยไม่ลดทอนโปรแกรม C ที่มีประโยชน์อย่างสมบูรณ์ที่ไม่ควรพกพา ดังนั้นคำวิเศษณ์อย่างเคร่งครัด

ดังนั้นโปรแกรมสกปรกเล็ก ๆ น้อย ๆ ที่ทำงานได้ดีอย่างสมบูรณ์แบบกับ GCC ยังคงสอดคล้อง !


15

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

พฤติกรรมที่ไม่ได้กำหนดอื่น ๆ สะท้อนถึงความเป็นจริง ตัวอย่างหนึ่งคือการเลื่อนบิตที่มีจำนวนมากกว่าประเภท ที่จริงแล้วแตกต่างระหว่างรุ่นฮาร์ดแวร์ของตระกูลเดียวกัน หากคุณมีแอพ 16 บิตไบนารีเดียวกันที่แน่นอนจะให้ผลลัพธ์ที่แตกต่างกันใน 80286 และ 80386 ดังนั้นมาตรฐานภาษาบอกว่าเราไม่รู้!

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


+1 สำหรับย่อหน้าที่สองซึ่งแสดงบางสิ่งบางอย่างที่น่าอึดอัดใจที่ระบุว่าเป็นพฤติกรรมที่กำหนดโดยการนำไปปฏิบัติ
David Thornley

3
บิตกะเป็นเพียงตัวอย่างของการยอมรับพฤติกรรมของคอมไพเลอร์ที่ไม่ได้กำหนดและการใช้ความสามารถของฮาร์ดแวร์ มันจะไม่สำคัญที่จะระบุผลลัพธ์ C สำหรับการเลื่อนบิตเมื่อจำนวนมีขนาดใหญ่กว่าประเภท แต่มีราคาแพงในการใช้กับฮาร์ดแวร์บางตัว
mattnz

7

ในตัวอย่างหนึ่งการเข้าถึงตัวชี้เกือบจะต้องไม่ได้กำหนดและไม่จำเป็นต้องมีเพียงแค่เหตุผลด้านประสิทธิภาพเท่านั้น ตัวอย่างเช่นในบางระบบการโหลดการลงทะเบียนที่เฉพาะเจาะจงด้วยตัวชี้จะสร้างข้อยกเว้นฮาร์ดแวร์ ใน SPARC การเข้าถึงวัตถุหน่วยความจำที่จัดตำแหน่งไม่ถูกต้องจะทำให้เกิดข้อผิดพลาดบัส แต่ใน x86 มันจะ "เพียงแค่" จะช้า มันเป็นการยากที่จะระบุพฤติกรรมในกรณีเหล่านี้เนื่องจากฮาร์ดแวร์ที่มีพื้นฐานเป็นตัวกำหนดสิ่งที่จะเกิดขึ้นและ C ++ นั้นสามารถพกพาได้กับฮาร์ดแวร์หลายประเภท

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

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


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

6

ง่าย: ความเร็วและการพกพา หาก C ++ รับประกันว่าคุณได้รับข้อยกเว้นเมื่อยกเลิกการอ้างอิงตัวชี้ที่ไม่ถูกต้องจะไม่สามารถพกพาไปยังฮาร์ดแวร์ที่ฝังตัวได้ หาก C ++ รับประกันสิ่งอื่น ๆ เช่นการเตรียมใช้งานเบื้องต้นเสมอมันจะช้าลงและในช่วงเวลาที่เริ่มต้นของ C ++ ที่ช้าลงนั้นเป็นสิ่งที่แย่จริงๆ


1
ฮะ? ข้อยกเว้นเกี่ยวกับฮาร์ดแวร์แบบฝังตัวคืออะไร
Mason Wheeler

2
ข้อยกเว้นอาจล็อคระบบด้วยวิธีที่ไม่ดีสำหรับระบบสมองกลฝังตัวที่ต้องตอบสนองอย่างรวดเร็ว มีบางสถานการณ์ที่การอ่านที่ผิดพลาดนั้นสร้างความเสียหายได้น้อยกว่ามากซึ่งเป็นระบบที่ช้า
วิศวกรโลก

1
@ Mason: เพราะฮาร์ดแวร์ต้องจับการเข้าถึงที่ไม่ถูกต้อง มันง่ายสำหรับ Windows ที่จะโยนการละเมิดการเข้าถึงและยากขึ้นสำหรับฮาร์ดแวร์ฝังตัวที่ไม่มีระบบปฏิบัติการที่จะทำอะไรนอกจากตาย
DeadMG

3
อย่าลืมว่า CPU ทุกตัวมี MMU เพื่อป้องกันการเข้าถึงฮาร์ดแวร์ที่ไม่ถูกต้องในตอนแรก หากคุณต้องการให้ภาษาของคุณตรวจสอบการเข้าถึงตัวชี้ทั้งหมดคุณจะต้องจำลอง MMU บน CPU ที่ไม่มีเครื่องเดียวดังนั้นการเข้าถึงหน่วยความจำทุกครั้งจึงมีราคาแพงมาก
ปุย

4

C ถูกประดิษฐ์ขึ้นบนเครื่องที่มีขนาด 9 บิตและไม่มีหน่วยทศนิยมใด ๆ - สมมติว่ามันได้รับคำสั่งว่าไบต์คือ 9 บิต, คำ 18 บิตและลอยนั้นควรจะใช้งานโดยใช้ IEEE754 ก่อน


5
ฉันสงสัยว่าคุณกำลังนึกถึง Unix - C เดิมทีใช้ใน PDP-11 ซึ่งจริงๆแล้วเป็นมาตรฐานในปัจจุบันที่ค่อนข้างธรรมดา ฉันคิดว่าความคิดพื้นฐานยังคงอยู่
Jerry Coffin

@Jerry - ใช่คุณพูดถูก - ฉันแก่แล้ว!
Martin Beckett

ใช่ - เกิดขึ้นกับสิ่งที่ดีที่สุดของเราฉันกลัว
Jerry Coffin

4

ฉันไม่คิดว่าเหตุผลแรกสำหรับ UB คือการปล่อยให้ห้องคอมไพเลอร์เพิ่มประสิทธิภาพ แต่เป็นไปได้ที่จะใช้การนำไปปฏิบัติที่ชัดเจนสำหรับเป้าหมาย ณ เวลาที่สถาปัตยกรรมมีความหลากหลายมากกว่านี้ (จำไว้ว่า C ออกแบบบน PDP-11 ซึ่งมีสถาปัตยกรรมที่ค่อนข้างคุ้นเคยพอร์ตแรกคือHoneywell 635ซึ่งไม่คุ้นเคย - คำแอดเดรสใช้ 36 บิตคำ 6 หรือ 9 บิตไบต์ 18 บิตที่อยู่ ... อย่างน้อยก็ใช้ของ 2 เติมเต็ม) แต่ถ้าการเพิ่มประสิทธิภาพอย่างหนักไม่ใช่เป้าหมายการใช้งานที่ชัดเจนไม่รวมการเพิ่มการตรวจสอบรันไทม์สำหรับโอเวอร์โฟลว์จำนวนการเปลี่ยนแปลงที่เกินขนาดการลงทะเบียนชื่อแทนนั้นในนิพจน์ที่ปรับเปลี่ยนค่าหลายค่า

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


ฉันสงสัยว่าอะไรทำให้ปรัชญาการเปลี่ยนแปลงของ UB จาก "อนุญาตให้โปรแกรมเมอร์ใช้พฤติกรรมที่เปิดเผยโดยแพลตฟอร์มของพวกเขา" เป็น "ค้นหาข้อแก้ตัวเพื่อให้คอมไพเลอร์ใช้พฤติกรรมที่แปลกประหลาดโดยสิ้นเชิง" ฉันยังสงสัยว่าการเพิ่มประสิทธิภาพเช่นนั้นจะเพิ่มขนาดโค้ดหลังจากที่มีการแก้ไขโค้ดเพื่อทำงานภายใต้คอมไพเลอร์ใหม่หรือไม่ ฉันจะไม่แปลกใจถ้าในหลาย ๆ กรณีผลกระทบเพียงอย่างเดียวของการเพิ่ม "การเพิ่มประสิทธิภาพ" ให้กับคอมไพเลอร์คือการบังคับให้โปรแกรมเมอร์เขียนโค้ดที่ใหญ่และช้าลงเพื่อหลีกเลี่ยงการคอมไพเลอร์แตก
supercat

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

1
สิ่งที่ฉันอยากรู้คือคำแถลงที่ฉันได้เห็นถึงผลกระทบของ "C สันนิษฐานว่าโปรแกรมเมอร์ไม่เคยมีส่วนร่วมในพฤติกรรมที่ไม่ได้กำหนด" - ความจริงที่ไม่เคยมีมาก่อน คำสั่งที่ถูกต้องจะได้รับ "C สันนิษฐานว่าโปรแกรมเมอร์จะไม่ก่อให้เกิดพฤติกรรมที่ไม่ได้กำหนดโดยมาตรฐานเว้นแต่เตรียมที่จะจัดการกับผลกระทบที่แพลตฟอร์มธรรมชาติของพฤติกรรมที่. ระบุว่า C ได้รับการออกแบบเป็นภาษาที่ระบบการเขียนโปรแกรมเป็นส่วนใหญ่ของวัตถุประสงค์คือการอนุญาตให้โปรแกรมเมอร์ทำสิ่งต่าง ๆ เฉพาะของระบบที่ไม่ได้กำหนดโดยมาตรฐานภาษาความคิดที่พวกเขาไม่เคยทำนั้นเป็นเรื่องไร้สาระ
supercat

เป็นการดีที่โปรแกรมเมอร์จะต้องใช้ความพยายามเป็นพิเศษในการตรวจสอบความสะดวกในการพกพาในกรณีที่แพลตฟอร์มต่าง ๆ จะทำสิ่งที่แตกต่างกันโดยสิ้นเชิงแต่ผู้เขียนคอมไพเลอร์เสียเวลาของทุกคนเมื่อกำจัดพฤติกรรมที่โปรแกรมเมอร์ในอดีตคาดการณ์ไว้ว่า รับจำนวนเต็มiและnเช่นนั้นn < INT_BITSและi*(1<<n)จะไม่ล้นฉันจะถือว่าi<<=n;ชัดเจนกว่าi=(unsigned)i << n;; บนแพลตฟอร์มหลาย ๆ i*=(1<<N);คนก็จะได้เร็วขึ้นและมีขนาดเล็กกว่า อะไรคือสิ่งที่ทำให้คอมไพเลอร์ห้ามมิให้มัน?
supercat

ในขณะที่ฉันคิดว่ามันจะดีสำหรับมาตรฐานที่อนุญาตให้ใช้กับดักสำหรับสิ่งต่าง ๆ ที่เรียกว่า UB (เช่นจำนวนเต็มล้น) และมีเหตุผลที่ดีที่ไม่ต้องการให้กับดักทำสิ่งที่คาดเดาได้ฉันจะคิดว่าในทุกมุมมอง มาตรฐานจะได้รับการปรับปรุงหากจำเป็นต้องใช้รูปแบบส่วนใหญ่ของ UB จะต้องให้คุณค่าไม่ จำกัด หรือเอกสารข้อเท็จจริงที่ว่าพวกเขาขอสงวนสิทธิ์ที่จะทำอย่างอื่นโดยไม่จำเป็นต้องทำเอกสารอะไรที่อาจเป็นอย่างอื่น คอมไพเลอร์ซึ่งทำทุกอย่าง "UB" จะถูกกฎหมาย แต่น่าเสียดาย ...
supercat

3

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


การเพิ่มจำนวนเต็มเป็นกรณีที่น่าสนใจ นอกเหนือจากความเป็นไปได้ของพฤติกรรมการดักซึ่งในบางกรณีจะมีประโยชน์ แต่ในกรณีอื่นอาจทำให้เกิดการเรียกใช้โค้ดแบบสุ่มมีสถานการณ์ที่เหมาะสมสำหรับคอมไพเลอร์เพื่อทำการอ้างถึงตามความจริงที่ว่าจำนวนเต็มล้นไม่ได้ระบุไว้ ตัวอย่างเช่นคอมไพเลอร์ที่intมี 16 บิตและการเลื่อนแบบขยายสัญญาณมีราคาแพงสามารถคำนวณได้(uchar1*uchar2) >> 4โดยใช้การเลื่อนแบบไม่เซ็นสัญญา น่าเสียดายที่คอมไพเลอร์บางตัวขยายการอนุมานไม่เพียง แต่ผลลัพธ์ แต่รวมถึงตัวถูกดำเนินการ
supercat

2

ฉันบอกว่ามันเกี่ยวกับปรัชญาน้อยกว่าที่เกี่ยวกับความเป็นจริง - C เป็นภาษาข้ามแพลตฟอร์มเสมอและมาตรฐานต้องสะท้อนให้เห็นว่าและความจริงที่ว่าในเวลาที่มีการเผยแพร่มาตรฐานใด ๆ จะมี การนำไปใช้งานจำนวนมากบนฮาร์ดแวร์ที่แตกต่างกันมากมาย มาตรฐานที่ห้ามพฤติกรรมที่จำเป็นอาจถูกเพิกเฉยหรือสร้างมาตรฐานที่แข่งขันได้


ในขั้นต้นพฤติกรรมหลายอย่างถูกทิ้งไว้โดยไม่ได้กำหนดเพื่อให้ความเป็นไปได้ที่ระบบต่าง ๆ จะทำสิ่งที่แตกต่างกันรวมถึงทริกเกอร์กับฮาร์ดแวร์ที่มีตัวจัดการที่อาจจะหรืออาจจะไม่สามารถกำหนดค่าได้ ต้องการให้การเลื่อนซ้ายของค่าลบไม่ใช่กับดักตัวอย่างเช่นจะทำลายโค้ดใด ๆ ที่ออกแบบมาสำหรับระบบที่มันทำและพึ่งพาพฤติกรรมดังกล่าว ในระยะสั้นจะถูกทิ้งไม่ได้กำหนดเพื่อที่จะไม่ป้องกันการพัฒนาระบบจากพฤติกรรมให้พวกเขาคิดว่าเป็นประโยชน์
supercat

แต่น่าเสียดายที่มีการบิดรอบเช่นนั้นแม้รหัสที่รู้ว่ามันทำงานบนโปรเซสเซอร์ที่จะทำสิ่งที่มีประโยชน์ในกรณีเฉพาะไม่สามารถใช้ประโยชน์จากพฤติกรรมดังกล่าวเพราะคอมไพเลอร์อาจใช้ความจริงที่ว่ามาตรฐาน C ไม่ได้ ไม่ระบุพฤติกรรม (แม้ว่าจะใช้แพลตฟอร์ม) เพื่อใช้การเขียนที่แปลกประหลาดของโลกกับรหัส
supercat

1

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


หรือคุณสามารถจัดสรรตัวชี้ทั้งหมดเป็นweak_ptrและลบล้างการอ้างอิงทั้งหมดไปยังตัวชี้ที่ได้รับdeleted ... โอ้เดี๋ยวก่อนเรากำลังเข้าสู่การเก็บขยะ: /
Matthieu M.

boost::weak_ptrการใช้งานเป็นเทมเพลตที่ดีพอที่จะเริ่มต้นสำหรับรูปแบบการใช้งานนี้ แทนที่จะติดตามและลบล้างweak_ptrsจากภายนอกสิ่งที่weak_ptrมีส่วนช่วยในการshared_ptrนับที่อ่อนแอของและการนับที่อ่อนแอนั้นเป็น refcount กับตัวชี้ ดังนั้นคุณสามารถทำให้เป็นโมฆะshared_ptrโดยไม่ต้องลบทันที มันไม่สมบูรณ์แบบ (คุณยังสามารถมีจำนวนมากที่หมดอายุการweak_ptrบำรุงรักษาพื้นฐานโดยshared_countไม่มีเหตุผลที่ดี) แต่อย่างน้อยก็รวดเร็วและมีประสิทธิภาพ
ปุย

0

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

โดยพื้นฐานแล้วคุณสามารถเลือกได้สองแบบ:

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

2) มีชุดของกฎสำหรับเมื่อตัวชี้ได้รับอนุญาตให้ใช้นามแฝงตัวแปรและเมื่อคอมไพเลอร์ได้รับอนุญาตให้สมมติว่าตัวชี้ไม่ได้ใช้นามแฝงตัวแปร

ตัวเลือก C สำหรับตัวเลือก 2 เนื่องจาก 1 จะแย่มากสำหรับประสิทธิภาพ แต่แล้วจะเกิดอะไรขึ้นถ้าตัวชี้นามแฝงตัวแปรในวิธีที่กฎ C ห้าม? เนื่องจากผลกระทบนั้นขึ้นอยู่กับว่าคอมไพเลอร์ได้เก็บตัวแปรไว้ในทะเบียนจริงหรือไม่จึงไม่มีวิธีใดที่มาตรฐาน C จะรับประกันผลลัพธ์ที่แน่นอนได้อย่างแน่นอน


จะมีความแตกต่างทางความหมายระหว่างการพูดว่า "คอมไพเลอร์ได้รับอนุญาตให้ทำตัวราวกับว่า X เป็นจริง" และพูดว่า "โปรแกรมใด ๆ ที่ X ไม่เป็นความจริงจะมีส่วนร่วมในพฤติกรรมที่ไม่ได้กำหนด" แต่น่าเสียดายที่มาตรฐาน ในหลาย ๆ สถานการณ์รวมถึงตัวอย่างนามแฝงของคุณคำสั่งเดิมจะอนุญาตให้มีการปรับแต่งคอมไพเลอร์จำนวนมากซึ่งจะเป็นไปไม่ได้ ส่วนหลังอนุญาตให้ "การปรับให้เหมาะสม" เพิ่มขึ้นได้บ้าง แต่การปรับให้เหมาะสมส่วนหลังนั้นเป็นสิ่งที่โปรแกรมเมอร์ไม่ต้องการ
supercat

ตัวอย่างเช่นถ้าบางรหัสตั้งค่าเป็นfoo42 แล้วเรียกวิธีการที่ใช้ตัวชี้ที่แก้ไขอย่างผิดกฎหมายเพื่อตั้งค่าfooเป็น 44 ฉันจะเห็นประโยชน์ที่จะบอกว่าจนกว่าการเขียน "ถูกต้อง" fooครั้งต่อไปความพยายามในการอ่านอาจถูกต้องตามกฎหมาย ให้ผลตอบแทน 42 หรือ 44 และการแสดงออกเช่นfoo+fooนี้อาจให้ผล 86 แต่ฉันเห็นประโยชน์น้อยกว่ามากในการอนุญาตให้คอมไพเลอร์ทำการอ้างถึงแบบขยายและย้อนหลังการเปลี่ยนพฤติกรรมที่ไม่ได้กำหนดซึ่งพฤติกรรมที่เป็นไปได้ทั้งหมด เพื่อสร้างรหัสไร้สาระ
supercat

0

ในอดีตพฤติกรรมที่ไม่ได้กำหนดมีวัตถุประสงค์หลักสองประการ:

  1. เพื่อหลีกเลี่ยงการกำหนดให้ผู้เขียนคอมไพเลอร์สร้างรหัสเพื่อจัดการกับเงื่อนไขที่ไม่ควรเกิดขึ้น

  2. เพื่อให้เป็นไปได้ว่าในกรณีที่ไม่มีรหัสที่จะจัดการกับเงื่อนไขดังกล่าวอย่างชัดเจนการใช้งานอาจมีพฤติกรรม "ธรรมชาติ" หลายประเภทซึ่งในบางกรณีจะมีประโยชน์

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

น่าเสียดายที่ผู้เขียนคอมไพเลอร์บางคนยึดถือปรัชญาที่ผู้รวบรวมควรออกไปเพื่อค้นหาเงื่อนไขที่จะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดและสันนิษฐานว่าสถานการณ์ดังกล่าวอาจไม่เกิดขึ้น ดังนั้นในระบบที่มี 32- บิตintรหัสที่ได้รับเช่น:

uint32_t foo(uint16_t q, int *p)
{
  if (q > 46340)
    *p++;
  return q*q;
}

มาตรฐาน C จะอนุญาตให้คอมไพเลอร์บอกว่าถ้า q เป็น 46341 หรือใหญ่กว่านั้นนิพจน์ q * q จะให้ผลลัพธ์ที่มีขนาดใหญ่เกินไปที่จะใส่ลงไปในintดังนั้นจึงก่อให้เกิดพฤติกรรมที่ไม่ได้กำหนดและเป็นผลให้คอมไพเลอร์มีสิทธิ์สันนิษฐานว่า ไม่สามารถเกิดขึ้นได้ดังนั้นจึงไม่จำเป็นต้องเพิ่มขึ้น*pหากทำได้ หากรหัสการโทรใช้*pเป็นตัวบ่งชี้ว่ามันควรจะทิ้งผลลัพธ์ของการคำนวณผลของการปรับให้เหมาะสมอาจจะใช้รหัสซึ่งจะให้ผลลัพธ์ที่สมเหตุสมผลบนระบบที่ทำงานในรูปแบบใด ๆ ก็ตามที่มีจำนวนเต็มล้น (การดักจับอาจ น่าเกลียด แต่อย่างน้อยก็สมเหตุสมผล) และเปลี่ยนเป็นรหัสซึ่งอาจทำงานได้ไร้สาระ


-6

ประสิทธิภาพเป็นข้อแก้ตัวตามปกติ แต่ไม่ว่าจะเป็นข้อแก้ตัวหรือพฤติกรรมที่ไม่ได้กำหนดเป็นความคิดที่แย่มากสำหรับการพกพา ในพฤติกรรมที่ไม่ได้กำหนดผลกระทบจะกลายเป็นไม่ได้ตรวจสอบสมมติฐานที่ไม่ได้ระบุ


7
OP ระบุสิ่งนี้: "คำถามของฉันไม่เกี่ยวกับพฤติกรรมที่ไม่ได้กำหนดหรือไม่ดีจริง ๆ ฉันรู้ว่าอันตรายและคำพูดพฤติกรรมที่ไม่ได้กำหนดที่เกี่ยวข้องส่วนใหญ่จากมาตรฐานดังนั้นโปรดงดเว้นการโพสต์คำตอบเกี่ยวกับความเลว ." ดูเหมือนว่าคุณไม่ได้อ่านคำถาม
Etienne de Martel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.