การใช้ภาษาที่เข้มงวดมากขึ้นไม่เพียง แต่ทำให้การย้ายโพสต์เป้าหมายไปจากการติดตั้งใช้งานที่ถูกต้องไปจนถึงการทำให้สเป็คถูกต้อง มันยากที่จะทำบางสิ่งที่ผิดปกติมาก ซึ่งเป็นเหตุผลที่คอมไพเลอร์จับข้อบกพร่องมากมาย
เลขคณิตของตัวชี้เนื่องจากเป็นสูตรปกตินั้นไม่ปลอดภัยเนื่องจากระบบประเภทไม่ได้หมายความว่าควรจะหมายถึงอะไร คุณสามารถหลีกเลี่ยงปัญหานี้ได้อย่างสมบูรณ์ด้วยการทำงานในภาษาที่รวบรวมขยะ (วิธีการปกติที่ทำให้คุณต้องจ่ายค่า abstraction) หรือคุณอาจเจาะจงมากขึ้นเกี่ยวกับประเภทของพอยน์เตอร์ที่คุณใช้เพื่อให้คอมไพเลอร์สามารถปฏิเสธสิ่งที่ไม่สอดคล้องกันหรือไม่สามารถพิสูจน์ได้ว่าถูกต้องตามที่เขียนไว้ นี่คือวิธีการของบางภาษาเช่น Rust
ประเภทที่สร้างขึ้นเทียบเท่ากับบทพิสูจน์ดังนั้นหากคุณเขียนระบบประเภทที่ลืมสิ่งนี้สิ่งต่าง ๆ ทุกอย่างจะผิดพลาด สมมติว่าในขณะที่เมื่อเราประกาศประเภทเราจริง ๆ แล้วหมายความว่าเรากำลังยืนยันความจริงเกี่ยวกับสิ่งที่อยู่ในตัวแปร
- int * x; // การยืนยันผิด ๆ x มีอยู่และไม่ได้ชี้ไปที่ int
- int * y = z; // เฉพาะจริงถ้า z พิสูจน์แล้วว่าชี้ไปที่ int
- * (x + 3) = 5; // เฉพาะจริงถ้า (x + 3) ชี้ไปที่ int ในอาร์เรย์เดียวกันกับ x
- int c = a / b; // เฉพาะจริงถ้า b ไม่ใช่ศูนย์เช่น: "nonzero int b = ... ;"
- nullable int * z = NULL; // nullable int * ไม่เหมือนกับ int *
- int d = * z; // การยืนยันที่ผิดเนื่องจาก z เป็นโมฆะ
- if (z! = NULL) {int * e = z; } // Ok เพราะ z ไม่ใช่ null
- ฟรี (y); int w = * y; // การยืนยันผิด ๆ เนื่องจาก y ไม่มีอยู่อีกต่อไปที่ w
ในโลกนี้พอยน์เตอร์ไม่สามารถเป็นโมฆะได้ ไม่มีการกำหนด NerePointer และไม่ต้องตรวจสอบพอยน์เตอร์เพื่อหาค่าว่าง แต่ "nullable int *" เป็นประเภทที่แตกต่างกันซึ่งสามารถแยกค่าเป็น null หรือชี้ได้ ซึ่งหมายความว่า ณ จุดที่การสันนิษฐานที่ไม่เป็นโมฆะเริ่มต้นคุณจะเข้าสู่การยกเว้นของคุณหรือลงไปที่สาขาที่ว่าง
ในโลกนี้มีข้อผิดพลาดมากมายที่ไม่มีขอบเขตเช่นกัน หากคอมไพเลอร์ไม่สามารถพิสูจน์ได้ว่ามีขอบเขตแล้วให้ลองเขียนใหม่เพื่อให้คอมไพเลอร์สามารถพิสูจน์ได้ หากไม่สามารถทำได้คุณจะต้องใส่ Assumption ด้วยตนเอง ณ จุดนั้น คอมไพเลอร์อาจพบความขัดแย้งในภายหลัง
นอกจากนี้หากคุณไม่มีตัวชี้ที่ไม่ได้กำหนดค่าเริ่มต้นคุณจะไม่มีตัวชี้ไปยังหน่วยความจำที่ไม่ได้เตรียมการ หากคุณมีตัวชี้ไปยังหน่วยความจำที่ว่างก็ควรจะถูกปฏิเสธโดยคอมไพเลอร์ ใน Rust มีพอยน์เตอร์หลายแบบที่จะทำให้การพิสูจน์ประเภทนี้เหมาะสมกับการคาดหวัง มีพอยน์เตอร์ที่เป็นเจ้าของโดยเฉพาะ (เช่น: ไม่มีนามแฝง), พอยน์เตอร์ไปยังโครงสร้างที่ไม่เปลี่ยนรูปอย่างลึกซึ้ง ประเภทการจัดเก็บเริ่มต้นไม่เปลี่ยนรูป ฯลฯ
นอกจากนี้ยังมีปัญหาในการบังคับใช้ไวยากรณ์ที่กำหนดขึ้นจริงบนโปรโตคอล (ซึ่งรวมถึงสมาชิกส่วนต่อประสาน) เพื่อ จำกัด พื้นที่พื้นผิวอินพุตให้ตรงกับที่คาดการณ์ไว้ สิ่งที่เกี่ยวกับ "ความถูกต้อง" คือ 1) ได้รับการกำจัดของทุกรัฐที่ไม่ได้กำหนด 2) ตรวจสอบให้แน่ใจตรรกะความสอดคล้อง ความยากลำบากในการเดินทางมีมากเกี่ยวกับการใช้เครื่องมือที่ไม่ดีอย่างยิ่ง (จากมุมมองของความถูกต้อง)
นี่คือเหตุผลที่สองแนวทางปฏิบัติที่เลวร้ายที่สุดคือตัวแปรทั่วโลกและ gotos สิ่งเหล่านี้ป้องกันการวางเงื่อนไขก่อน / หลัง / คงที่รอบ ๆ สิ่งใด นอกจากนี้ยังเป็นสาเหตุที่ทำให้ประเภทมีประสิทธิภาพ เมื่อประเภทแข็งแกร่งขึ้น (ในที่สุดใช้ประเภทอ้างอิงเพื่อนำมูลค่าที่แท้จริงมาพิจารณา) พวกเขาเข้าใกล้ถึงการพิสูจน์ความถูกต้องเชิงสร้างสรรค์ในตัวเอง การทำให้โปรแกรมไม่สอดคล้องกันล้มเหลวในการรวบรวม
โปรดจำไว้ว่ามันไม่ได้เป็นเพียงแค่ความผิดพลาดโง่ นอกจากนี้ยังเกี่ยวกับการปกป้องฐานรหัสจากผู้บุกรุกที่ฉลาด จะมีหลายกรณีที่คุณต้องปฏิเสธการส่งโดยไม่มีการพิสูจน์คุณสมบัติที่สำคัญที่สร้างโดยเครื่องจักรที่น่าเชื่อถือเช่น "ตามโปรโตคอลที่ระบุอย่างเป็นทางการ"