อ่าน 8 นาที
ประเภทความปลอดภัย
ใน วิทยาการคอมพิวเตอร์ ความ ปลอดภัยของชนิดข้อมูล หมายถึง ระดับที่ ภาษาโปรแกรมนั้นๆ ลดหรือป้องกัน ข้อผิดพลาดเกี่ยวกับชนิดข้อมูล ภาษาที่มีความปลอดภัยของชนิดข้อมูลสูงบางครั้งเรียกว่า...
ประเภทความปลอดภัย
| ระบบประเภท |
|---|
| แนวคิดทั่วไป |
| หมวดหมู่หลัก |
|
| หมวดหมู่ย่อย |
ในวิทยาการคอมพิวเตอร์ความปลอดภัยของชนิดข้อมูลหมายถึง ระดับที่ภาษาโปรแกรมนั้นๆลดหรือป้องกันข้อผิดพลาดเกี่ยวกับชนิดข้อมูลภาษาที่มีความปลอดภัยของชนิดข้อมูลสูงบางครั้งเรียกว่า ภาษา ที่มีการกำหนดชนิดข้อมูล อย่างเข้มงวดหรือ ภาษาที่มีการกำหนดชนิดข้อมูล อย่างเคร่งครัด พฤติกรรมที่ภาษาโปรแกรมนั้นๆ จัดว่าเป็นข้อผิดพลาดเกี่ยวกับชนิดข้อมูล มัก จะ เป็นพฤติกรรมที่เกิดจากการพยายามดำเนินการกับค่าที่ไม่ใช่ชนิดข้อมูล ที่เหมาะสม เช่น การพยายามบวกสตริงกับจำนวนเต็ม
การบังคับใช้ประเภทอาจเป็นแบบคงที่ (ตรวจจับข้อผิดพลาดที่อาจเกิดขึ้นในระหว่างการคอมไพล์ ) แบบไดนามิก (เชื่อมโยงข้อมูลประเภทกับค่าในระหว่างการทำงานและตรวจสอบตามความจำเป็นเพื่อตรวจจับข้อผิดพลาดที่กำลังจะเกิดขึ้น) หรือการผสมผสานของทั้งสองแบบ[ 1 ]การบังคับใช้ประเภทแบบไดนามิกมักจะสามารถรันโปรแกรมที่ไม่ถูกต้องภายใต้การบังคับใช้แบบคงที่ได้ แต่ต้องแลกมาด้วยการเกิดข้อผิดพลาดในระหว่างการทำงาน
ในบริบทของระบบประเภทคงที่ (ที่กำหนดในระหว่างการคอมไพล์) ความปลอดภัยของประเภทมักเกี่ยวข้องกับ (เหนือสิ่งอื่นใด) การรับประกันว่าค่าสุดท้ายของนิพจน์ ใดๆ จะเป็นสมาชิกที่ถูกต้องของประเภทคงที่ของนิพจน์นั้น ข้อกำหนดที่แท้จริงนั้นซับซ้อนกว่านี้มาก — ดูตัวอย่างเช่นการกำหนดประเภทย่อยและพหุรูปเพื่อทำความเข้าใจความซับซ้อนเพิ่มเติม
คำจำกัดความ
โดยสัญชาตญาณแล้ว ความถูกต้องของรูปแบบตัวอักษรนั้นสามารถอธิบายได้ด้วยคำกล่าวที่คมคายของ Robin Milner ที่ว่า
- โปรแกรมที่มีประเภทถูกต้องจะไม่สามารถ "เกิดข้อผิดพลาด" ได้[ 2 ]
กล่าวอีกนัยหนึ่ง หากระบบประเภทข้อมูลนั้นถูกต้องสมบูรณ์นิพจน์ที่ระบบประเภทข้อมูลนั้นยอมรับจะต้องประเมินค่าออกมาเป็นค่าของประเภทข้อมูลที่เหมาะสม (แทนที่จะสร้างค่าของประเภทข้อมูลอื่นที่ไม่เกี่ยวข้อง หรือเกิดข้อผิดพลาดเกี่ยวกับประเภทข้อมูล) Vijay Saraswat ได้ให้คำจำกัดความที่เกี่ยวข้องดังต่อไปนี้:
- ภาษาจะปลอดภัยต่อประเภทหากการดำเนินการเพียงอย่างเดียวที่สามารถกระทำกับข้อมูลในภาษาได้คือการดำเนินการที่ได้รับอนุญาตจากประเภทของข้อมูล[ 3 ]
อย่างไรก็ตาม สิ่งที่หมายถึงอย่างแน่ชัดว่าโปรแกรมจะ "มีประเภทที่ถูกต้อง" หรือ "เกิดข้อผิดพลาด" นั้นขึ้นอยู่กับคุณสมบัติของความหมายเชิงสถิตและ เชิงพลวัต ซึ่งเป็นลักษณะเฉพาะของแต่ละภาษาการเขียนโปรแกรม ดังนั้น คำจำกัดความที่เป็นทางการที่แม่นยำของความถูกต้องของประเภทจึงขึ้นอยู่กับรูปแบบของความหมายเชิงรูปธรรมที่ใช้ในการกำหนดภาษา ในปี 1994 แอนดรูว์ ไรท์และ แมท เทียส เฟลไลเซนได้กำหนดสิ่งที่กลายเป็นคำจำกัดความมาตรฐานและเทคนิคการพิสูจน์สำหรับความปลอดภัยของประเภทในภาษาที่กำหนดโดยความหมายเชิงปฏิบัติการ [ 4 ]ซึ่งใกล้เคียงกับแนวคิดของความปลอดภัยของประเภทตามที่โปรแกรมเมอร์ส่วนใหญ่เข้าใจ ภายใต้แนวทางนี้ ความหมายของภาษาต้องมีคุณสมบัติสองประการต่อไปนี้จึงจะถือว่ามีความถูกต้องของประเภท:
- ความคืบหน้า
- โปรแกรมที่เขียนอย่างดีจะไม่ "ติดขัด": ทุกนิพจน์จะเป็นค่า อยู่แล้ว หรือสามารถลดทอนไปสู่ค่าได้ด้วยวิธีที่กำหนดไว้อย่างชัดเจน กล่าวอีกนัยหนึ่ง โปรแกรมจะไม่เข้าสู่สถานะที่ไม่แน่นอนซึ่งไม่สามารถเปลี่ยนสถานะต่อไปได้อีก
- การรักษา (หรือการลดขนาดหัวข้อ )
- หลังจากขั้นตอนการประเมินแต่ละครั้ง รูปแบบของแต่ละนิพจน์จะยังคงเหมือนเดิม (กล่าวคือ รูปแบบของมันจะถูกเก็บรักษาไว้ )
นอกจากนี้ ยังมีการเผยแพร่การรักษาอย่างเป็นทางการอื่นๆ อีกจำนวนหนึ่งเกี่ยวกับความถูกต้องของประเภทในแง่ของความหมายเชิงความหมายและความหมายเชิงปฏิบัติการเชิงโครงสร้าง[ 2 ] [ 5 ] [ 6 ]
ความสัมพันธ์กับความปลอดภัยรูปแบบอื่นๆ
โดยลำพังแล้ว ความถูกต้องของประเภทข้อมูลเป็นคุณสมบัติที่ค่อนข้างอ่อนแอ เนื่องจากโดยพื้นฐานแล้วมันเพียงแค่ระบุว่ากฎของระบบประเภทข้อมูลนั้นมีความสอดคล้องกันภายในและไม่สามารถถูกบิดเบือนได้ อย่างไรก็ตาม ในทางปฏิบัติ ภาษาโปรแกรมถูกออกแบบมาเพื่อให้ความถูกต้องของประเภทข้อมูลยังรวมถึงคุณสมบัติที่แข็งแกร่งกว่าอื่นๆ ด้วย ซึ่งบางส่วนได้แก่:
- การป้องกันการดำเนินการที่ผิดกฎหมาย ตัวอย่างเช่น ระบบประเภทข้อมูลอาจปฏิเสธนิพจน์
3 / "Hello, World"ว่าไม่ถูกต้อง เนื่องจากตัวดำเนินการ หาร ไม่ได้ถูกกำหนดไว้สำหรับตัวหารสตริง - ความปลอดภัยของหน่วยความจำ
- ระบบการกำหนดชนิดข้อมูลสามารถป้องกันพอยเตอร์ที่ไม่ถูกต้องซึ่งอาจเกิดขึ้นได้จากการที่พอยเตอร์ชี้ไปยังวัตถุชนิดหนึ่งถูกมองว่าเป็นพอยเตอร์ชี้ไปยังวัตถุอีกชนิดหนึ่ง
- ระบบประเภทที่ซับซ้อนกว่า เช่น ระบบที่รองรับประเภทที่ขึ้นอยู่กันสามารถตรวจจับและปฏิเสธการเข้าถึงที่อยู่นอกขอบเขต ป้องกันบัฟเฟอร์โอเวอร์โฟลว์ที่อาจเกิดขึ้นได้[ 7 ]
- ข้อผิดพลาดเชิงตรรกะที่เกิดจากความหมายของชนิดข้อมูลที่แตกต่างกัน ตัวอย่างเช่น นิ้วและมิลลิเมตรอาจถูกจัดเก็บเป็นจำนวนเต็มได้ แต่ไม่ควรนำมาใช้แทนกันหรือบวกกัน ระบบชนิดข้อมูลสามารถบังคับใช้ชนิดข้อมูลจำนวนเต็มที่แตกต่างกันสองชนิดสำหรับหน่วยวัดทั้งสองนี้ได้
ภาษาที่มีประเภทข้อมูลปลอดภัยและภาษาที่มีประเภทข้อมูลไม่ปลอดภัย
ความปลอดภัยของประเภทมักเป็นข้อกำหนดสำหรับภาษาของเล่นใดๆ (เช่นภาษาเฉพาะกลุ่ม ) ที่เสนอในการวิจัยภาษาโปรแกรมเชิงวิชาการ อย่างไรก็ตาม ภาษาหลายภาษามีขนาดใหญ่เกินกว่าจะพิสูจน์ความปลอดภัยของประเภทที่สร้างโดยมนุษย์ได้ เนื่องจากมักจะต้องตรวจสอบหลายพันกรณี ถึงกระนั้น ภาษาบางภาษา เช่นStandard MLซึ่งมีนิยามความหมายที่เข้มงวด ได้รับการพิสูจน์แล้วว่าตรงตามนิยามความปลอดภัยของประเภทข้อหนึ่ง[ 8 ]ภาษาอื่นๆ บางภาษา เช่นHaskellเชื่อกันว่าตรงตามนิยามความปลอดภัยของประเภทบางประการ หากไม่ได้ใช้คุณลักษณะ "การหลีกหนี" บางอย่าง (ตัวอย่างเช่นunsafePerformIO ของ Haskell ซึ่งใช้เพื่อ "หลีกหนี" จากสภาพแวดล้อมที่จำกัดตามปกติซึ่งการป้อนข้อมูล/เอาต์พุต (I/O) เป็นไปได้ จะหลีกเลี่ยงระบบประเภทและสามารถใช้เพื่อทำลายความปลอดภัยของประเภทได้[ 9 ] ) การเล่นคำประเภทเป็นอีกตัวอย่างหนึ่งของคุณลักษณะ "การหลีกหนี" ดังกล่าว ไม่ว่าคุณสมบัติของนิยามภาษาจะเป็นอย่างไร ข้อผิดพลาดบางอย่างอาจเกิดขึ้นในขณะรันไทม์เนื่องจากข้อบกพร่องในการใช้งาน หรือในไลบรารี ที่เชื่อมโยง ซึ่งเขียนด้วยภาษาอื่นๆ ข้อผิดพลาดดังกล่าวอาจทำให้ประเภทการใช้งานที่กำหนดไม่ปลอดภัยในบางสถานการณ์ เครื่องเสมือน Javaเวอร์ชันแรกของ Sun มีความเสี่ยงต่อปัญหาประเภทนี้[ 3 ]
การพิมพ์แบบแข็งแรงและแบบอ่อนแอ
โดยทั่วไปแล้ว ภาษาโปรแกรมมักถูกจัดประเภทเป็นภาษาที่มีประเภทที่เข้มงวดหรือภาษาที่มีประเภทที่อ่อนแอ (หรือเรียกอีกอย่างว่าภาษาที่มีประเภทหลวม) เพื่ออ้างถึงแง่มุมบางประการของความปลอดภัยของประเภท ในปี 1974 Liskovและ Zilles ได้นิยามภาษาที่มีประเภทที่เข้มงวดว่าเป็นภาษาที่ "เมื่อใดก็ตามที่วัตถุถูกส่งผ่านจากฟังก์ชันที่เรียกไปยังฟังก์ชันที่ถูกเรียก ประเภทของวัตถุนั้นจะต้องเข้ากันได้กับประเภทที่ประกาศในฟังก์ชันที่ถูกเรียก" [ 10 ] ในปี 1977 Jackson เขียนว่า "ในภาษาที่มีประเภทที่เข้มงวด พื้นที่ข้อมูลแต่ละส่วนจะมีประเภทที่แตกต่างกัน และแต่ละกระบวนการจะระบุข้อกำหนดการสื่อสารในแง่ของประเภทเหล่านี้" [ 11 ] ในทางตรงกันข้าม ภาษาที่มีประเภทที่อ่อนแออาจสร้างผลลัพธ์ที่คาดเดาไม่ได้ หรืออาจทำการแปลงประเภทโดยปริยาย[ 12 ]
การจัดการหน่วยความจำและความปลอดภัยของประเภทข้อมูล
ความปลอดภัยของชนิดข้อมูลมีความเชื่อมโยงอย่างใกล้ชิดกับความปลอดภัยของหน่วยความจำตัวอย่างเช่น ในการใช้งานภาษาที่มีชนิดข้อมูลบางประเภทที่อนุญาตให้ใช้รูปแบบบิตบางอย่างแต่ไม่อนุญาตอย่างอื่น ข้อผิดพลาดของหน่วยความจำที่เกิดจากตัวชี้ที่ ค้างอยู่จะอนุญาตให้เขียนรูปแบบบิตที่ไม่แสดงถึงสมาชิกที่ถูกต้อง ลงใน ตัวแปร ที่ ตายแล้วของชนิดข้อมูลนั้นทำให้เกิดข้อผิดพลาดของชนิดข้อมูลเมื่ออ่านตัวแปรนั้น ในทางกลับกัน หากภาษานั้นมีความปลอดภัยของหน่วยความจำ ภาษาจะไม่ยอมให้ใช้จำนวนเต็มใดๆ เป็นตัวชี้ดังนั้นจึงต้องมีชนิดข้อมูลตัวชี้หรือชนิดข้อมูลอ้างอิงแยกต่างหาก
เงื่อนไขขั้นต่ำสุดของภาษาที่ปลอดภัยต่อประเภทข้อมูลคือ ต้องไม่อนุญาตให้มีตัวชี้ที่ชี้ไปยังตำแหน่งที่ไม่ถูกต้อง (dangling pointer)ข้ามการจัดสรรหน่วยความจำที่มีประเภทข้อมูลต่างกัน แต่ภาษาโปรแกรมส่วนใหญ่บังคับใช้การใช้ประเภทข้อมูลนามธรรมที่กำหนดโดยโปรแกรมเมอร์อย่างถูกต้อง แม้ว่าจะไม่จำเป็นอย่างเคร่งครัดต่อความปลอดภัยของหน่วยความจำหรือเพื่อป้องกันความล้มเหลวร้ายแรงใดๆ ก็ตาม การจัดสรรหน่วยความจำจะได้รับประเภทที่อธิบายเนื้อหาของมัน และประเภทนี้จะคงที่ตลอดระยะเวลาของการจัดสรร ซึ่งช่วยให้การวิเคราะห์นามแฝง ตามประเภทข้อมูลสามารถ อนุมานได้ว่าการจัดสรรหน่วยความจำที่มีประเภทข้อมูลต่างกันนั้นแตกต่างกัน
ภาษาที่มีความปลอดภัยต่อประเภทส่วนใหญ่ใช้การเก็บขยะเพียร์ซกล่าวว่า "การบรรลุความปลอดภัยต่อประเภทในกรณีที่มีการดำเนินการยกเลิกการจัดสรรอย่างชัดเจนนั้นเป็นเรื่องยากมาก" เนื่องจากปัญหาตัวชี้ที่ค้างอยู่[ 13 ]อย่างไรก็ตามRustโดยทั่วไปถือว่ามีความปลอดภัยต่อประเภทและใช้ตัวตรวจสอบการยืมเพื่อบรรลุความปลอดภัยต่อหน่วยความจำแทนการเก็บขยะ
ความปลอดภัยของประเภทข้อมูลในภาษาโปรแกรมเชิงวัตถุ
ใน ภาษา โปรแกรมเชิงวัตถุความปลอดภัยของประเภทข้อมูลมักเป็นสิ่งที่อยู่ภายในตัวมันเองอยู่แล้ว เนื่องจากมีระบบประเภทข้อมูลรองรับซึ่งแสดงออกมาในรูปของคำจำกัดความของคลาส
คลาสโดยพื้นฐานแล้วจะกำหนดโครงสร้างของอ็อบเจ็กต์ที่สืบทอดมาจากมัน และมีAPI เป็นข้อตกลงในการจัดการอ็อบเจ็กต์เหล่านั้น ทุกครั้งที่มีการสร้างอ็อบเจ็กต์ใหม่ อ็อบเจ็กต์นั้นจะต้องปฏิบัติตามข้อตกลงนั้น
แต่ละฟังก์ชันที่แลกเปลี่ยนวัตถุที่ได้มาจากคลาสเฉพาะ หรือใช้งานอินเทอร์เฟซ เฉพาะ จะต้องปฏิบัติตามสัญญาดังกล่าว ดังนั้นในฟังก์ชันนั้น การดำเนินการที่อนุญาตบนวัตถุนั้นจะมีเฉพาะการดำเนินการที่กำหนดโดยเมธอดของคลาสที่วัตถุนั้นใช้งานเท่านั้น ซึ่งจะรับประกันว่าความสมบูรณ์ของวัตถุจะได้รับการรักษาไว้[ 14 ]
ข้อยกเว้นสำหรับเรื่องนี้คือภาษาโปรแกรมเชิงวัตถุที่อนุญาตให้แก้ไขโครงสร้างของวัตถุแบบไดนามิก หรือการใช้การสะท้อน (reflection)เพื่อแก้ไขเนื้อหาของวัตถุเพื่อเอาชนะข้อจำกัดที่กำหนดโดยคำจำกัดความของเมธอดในคลาส
พิมพ์ประเด็นด้านความปลอดภัยในภาษาต่างๆ
อาดา
ภาษา Adaถูกออกแบบมาให้เหมาะสมกับระบบฝังตัวไดรเวอร์อุปกรณ์และการเขียนโปรแกรมระบบ รูปแบบอื่นๆ แต่ก็ยังส่งเสริมการเขียนโปรแกรมที่ปลอดภัยต่อประเภทข้อมูลด้วย เพื่อแก้ไขเป้าหมายที่ขัดแย้งกันเหล่านี้ Ada จึงจำกัดความไม่ปลอดภัยต่อประเภทข้อมูลไว้ในโครงสร้างพิเศษบางชุด ซึ่งชื่อของโครงสร้างเหล่านั้นมักเริ่มต้นด้วยสตริงUnchecked_ Unchecked_Deallocation สามารถถูกห้ามใช้ในหน่วยข้อความของ Ada ได้อย่างมีประสิทธิภาพโดยการใช้pragma Pureกับหน่วยนั้น คาดว่าโปรแกรมเมอร์จะใช้ โครงสร้าง Unchecked_อย่างระมัดระวังและเฉพาะเมื่อจำเป็นเท่านั้น โปรแกรมที่ไม่ใช้โครงสร้างเหล่านี้จะปลอดภัยต่อประเภทข้อมูล
ภาษา โปรแกรม SPARKเป็นส่วนย่อยของภาษา Ada ซึ่งขจัดความกำกวมและความไม่ปลอดภัยที่อาจเกิดขึ้นทั้งหมด ในขณะเดียวกันก็เพิ่มสัญญาที่ตรวจสอบได้แบบคงที่ให้กับคุณสมบัติของภาษา SPARK หลีกเลี่ยงปัญหาเกี่ยวกับ พอยเตอร์ที่ชี้ไปยังตำแหน่งที่ไม่ถูกต้อง (dangling pointers)โดยการไม่อนุญาตให้มีการจัดสรรหน่วยความจำในขณะรันไทม์โดยสิ้นเชิง
Ada2012 เพิ่มสัญญาที่ตรวจสอบได้แบบคงที่ให้กับตัวภาษาเอง (ในรูปแบบของเงื่อนไขก่อนและหลังการทำงาน รวมถึงตัวแปรคงที่ของประเภท)
ซี
ภาษาโปรแกรม Cมีความปลอดภัยด้านชนิดข้อมูลในบริบทที่จำกัด ตัวอย่างเช่น จะเกิดข้อผิดพลาดขณะคอมไพล์เมื่อพยายามแปลงพอยเตอร์ไปยังโครงสร้างชนิดหนึ่งเป็นพอยเตอร์ไปยังโครงสร้างอีกชนิดหนึ่ง เว้นแต่จะใช้การแปลงชนิดข้อมูลอย่างชัดเจน อย่างไรก็ตาม การดำเนินการทั่วไปจำนวนมากไม่ปลอดภัยด้านชนิดข้อมูล ตัวอย่างเช่น วิธีปกติในการพิมพ์จำนวนเต็มคือการใช้คำสั่ง printf printf("%d", 12)โดยที่คำ%dสั่ง printf บอกprintfฟังก์ชันขณะรันไทม์ว่าให้คาดหวังอาร์กิวเมนต์เป็นจำนวนเต็ม (เช่น `printf` printf("%s", 12)ซึ่งบอกฟังก์ชันว่าให้คาดหวังพอยเตอร์ไปยังสตริงอักขระ แต่กลับให้ค่าจำนวนเต็มเป็นอาร์กิวเมนต์ อาจได้รับการยอมรับจากคอมไพเลอร์ แต่จะให้ผลลัพธ์ที่ไม่แน่นอน) ปัญหานี้ได้รับการแก้ไขบางส่วนโดยคอมไพเลอร์บางตัว (เช่น gcc) ที่ตรวจสอบความสอดคล้องของชนิดข้อมูลระหว่างอาร์กิวเมนต์ของ printf และสตริงรูปแบบ
นอกจากนี้ C เช่นเดียวกับ Ada ยังมีการแปลงแบบชัดเจนที่ไม่ระบุหรือไม่นิยามไว้ และแตกต่างจากใน Ada สำนวนที่ใช้การแปลงเหล่านี้พบได้ทั่วไป และช่วยให้ C มีชื่อเสียงในด้านความไม่ปลอดภัยของประเภท ตัวอย่างเช่น วิธีมาตรฐานในการจัดสรรหน่วยความจำบนฮีปคือการเรียกใช้ฟังก์ชันการจัดสรรหน่วยความจำ เช่นmallocโดยมีอาร์กิวเมนต์ที่ระบุจำนวนไบต์ที่ต้องการ ฟังก์ชันจะส่งคืนพอย เตอร์แบบไม่มีเงื่อนไข ( void pointer ) void*ซึ่งโค้ดที่เรียกใช้จะต้องแปลงแบบชัดเจนหรือโดยปริยายเป็นประเภทพอยเตอร์ที่เหมาะสม การใช้งาน C ก่อนการกำหนดมาตรฐานจำเป็นต้องมีการแปลงแบบชัดเจนเพื่อทำเช่นนั้น ดังนั้นสำหรับการจัดสรรบางค่าstruct Fooโค้ดจึงกลายเป็นแนวปฏิบัติที่ยอมรับได้[ 15 ]ส่งคืนซึ่งไม่จำเป็นต้องแปลงแบบชัดเจนใน C แต่ใน C++ การแปลงนี้ถูกทำให้เป็นข้อบังคับเพื่อความปลอดภัยของประเภท Foo* foo = (struct Foo*)malloc(sizeof(struct Foo))malloc()void*
ซี++
โดยทั่วไป C++มีความปลอดภัยด้านประเภทมากกว่า C ซึ่งเป็นรุ่นก่อนหน้า ด้วยคุณสมบัติต่างๆ เช่น ไม่อนุญาตให้แปลงประเภทตัวชี้โดยปริยายจากvoid*ประเภทตัวชี้อื่น[ 16 ]และคุณสมบัติอื่นๆ เช่น:
- ตัว ดำเนินการ newจะส่งคืนพอยเตอร์ที่มีชนิดตามตัวถูกดำเนินการ ในขณะที่mallocจะส่งคืนพอยเตอร์แบบ void
- โค้ด C++ สามารถใช้ฟังก์ชันเสมือนและเทมเพลตเพื่อให้ได้คุณสมบัติโพลีมอร์ฟิซึมที่ปลอดภัยต่อประเภทข้อมูลโดยไม่ต้องใช้พอยเตอร์แบบ void
- ตัวดำเนินการแปลงประเภทที่ปลอดภัยกว่า เช่น
dynamic_castตัวดำเนินการที่ทำการตรวจสอบประเภทในขณะรันไทม์ระหว่างตัวชี้และตัวอ้างอิง ในขณะที่static_castตัวดำเนินการที่ทำการตรวจสอบประเภทในขณะคอมไพล์ระหว่างประเภทที่จะถูกแปลงไปเป็นประเภทอื่น ซึ่งโดยทั่วไปแล้วจะปลอดภัยกว่าการแปลงประเภทแบบ C - ใน C++11 การแจงนับแบบกำหนดประเภทอย่างเข้มงวด (หรือที่เรียกว่า
enum class'es') ไม่สามารถแปลงโดยปริยายไปเป็นหรือจากจำนวนเต็มหรือประเภทการแจงนับอื่นๆ ได้ - ตัวสร้างแบบระบุชัดเจนใน C++ และตัวดำเนินการแปลงแบบระบุชัดเจนใน C++11ป้องกันการแปลงประเภทโดยปริยาย
ซี#
C#เป็นภาษาที่ปลอดภัยต่อชนิดข้อมูล (type-safe) รองรับพอยเตอร์ที่ไม่มีชนิดข้อมูล (untyped pointers) แต่ต้องเข้าถึงโดยใช้คีย์เวิร์ด "unsafe" ซึ่งสามารถห้ามใช้ได้ในระดับคอมไพเลอร์ C# รองรับการตรวจสอบความถูกต้องของการแปลงชนิดข้อมูลในขณะรันไทม์ (run-time cast validation) สามารถตรวจสอบความถูกต้องได้โดยใช้คีย์เวิร์ด "as" ซึ่งจะคืนค่าเป็น null หากการแปลงชนิดข้อมูลไม่ถูกต้อง หรือโดยใช้การแปลงชนิดข้อมูลแบบ C-style ซึ่งจะโยนข้อยกเว้นหากการแปลงชนิดข้อมูลไม่ถูกต้อง ดูที่ ตัวดำเนิน การ แปลงชนิดข้อมูลใน C#
การพึ่งพา ประเภท อ็อบเจ็กต์ มากเกินไป (ซึ่งเป็นที่มาของประเภทอื่นๆ ทั้งหมด) อาจทำให้จุดประสงค์ของระบบประเภทใน C# เสียไป โดยทั่วไปแล้วควรละทิ้งการอ้างอิงอ็อบเจ็กต์และหันมาใช้เจเนริก แทน คล้ายกับเทมเพลตใน C++ และเจเนริกใน Java
ชวา
ภาษาJavaถูกออกแบบมาเพื่อบังคับใช้ความปลอดภัยของประเภทข้อมูล ทุกสิ่งใน Java เกิด ขึ้นภายในอ็อบเจ็กต์ และแต่ละอ็อบเจ็กต์เป็นอินสแตนซ์ของคลาส
เพื่อให้ การบังคับใช้ ความปลอดภัยของประเภท ข้อมูลเป็นไป อย่างมีประสิทธิภาพ วัตถุแต่ละชิ้นจะต้องได้รับการจัดสรรพื้นที่ ก่อนใช้งาน ภาษาจาวาอนุญาตให้ใช้ประเภทข้อมูลพื้นฐานได้แต่เฉพาะภายในวัตถุที่ได้รับการจัดสรรพื้นที่อย่างถูกต้องเท่านั้น
บางครั้ง ความปลอดภัยของชนิดข้อมูลบางส่วนถูกนำไปใช้โดยอ้อม เช่น คลาส BigDecimal แทนจำนวนทศนิยมที่มีความแม่นยำสูง แต่รองรับเฉพาะจำนวนที่สามารถแสดงได้ด้วยรูปแบบจำกัดเท่านั้น การดำเนินการ BigDecimal.divide() จะคำนวณวัตถุใหม่โดยการหารจำนวนสองจำนวนที่แสดงในรูปแบบ BigDecimal
ในกรณีนี้ หากการหารไม่มีการแสดงผลแบบจำกัด เช่น เมื่อคำนวณ 1/3 = 0.33333... เมธอด divide() อาจส่งข้อผิดพลาดหากไม่ได้กำหนดโหมดการปัดเศษสำหรับการดำเนินการ ดังนั้นไลบรารีจึงเป็นตัวรับประกันว่าวัตถุนั้นเคารพข้อตกลงโดยนัยในคำจำกัดความของคลาส มากกว่าตัวภาษาเอง
มาตรฐาน ML
ภาษา ML มาตรฐานมีความหมายที่กำหนดไว้อย่างเข้มงวดและเป็นที่ทราบกันดีว่าปลอดภัยต่อประเภทข้อมูล อย่างไรก็ตาม การใช้งานบางอย่าง รวมถึงStandard ML of New Jersey (SML/NJ) ตัวแปรทางไวยากรณ์MythrylและMLtonมีไลบรารีที่ให้การดำเนินการที่ไม่ปลอดภัย ฟังก์ชันเหล่านี้มักใช้ร่วมกับอินเทอร์เฟซฟังก์ชันภายนอก ของการใช้งานเหล่านั้น เพื่อโต้ตอบกับโค้ดที่ไม่ใช่ ML (เช่น ไลบรารี C) ที่อาจต้องการข้อมูลที่จัดเรียงในลักษณะเฉพาะ อีกตัวอย่างหนึ่งคือระดับบนสุดแบบโต้ตอบ ของ SML/NJ เอง ซึ่งต้องใช้การดำเนินการที่ไม่ปลอดภัยเพื่อเรียกใช้โค้ด ML ที่ผู้ใช้ป้อน
โมดูลา-2
Modula-2 เป็นภาษาที่มีการกำหนดประเภทอย่างเข้มงวด โดยมีปรัชญาการออกแบบที่กำหนดให้ฟังก์ชันที่ไม่ปลอดภัยใดๆ ต้องถูกทำเครื่องหมายว่าไม่ปลอดภัยอย่างชัดเจน ทำได้โดยการ "ย้าย" ฟังก์ชันดังกล่าวไปยังไลบรารีเสมือนในตัวที่เรียกว่า SYSTEM ซึ่งจะต้องนำเข้าก่อนจึงจะสามารถใช้งานได้ การนำเข้าจึงทำให้มองเห็นได้เมื่อมีการใช้งานฟังก์ชันดังกล่าว น่าเสียดายที่สิ่งนี้ไม่ได้ถูกนำไปใช้ในรายงานภาษาฉบับดั้งเดิมและการใช้งาน[ 17 ]ยังคงมีสิ่งอำนวยความสะดวกที่ไม่ปลอดภัย เช่น ไวยากรณ์การแปลงประเภทและบันทึกตัวแปร (ที่สืบทอดมาจาก Pascal) ที่สามารถใช้งานได้โดยไม่ต้องนำเข้าก่อน[ 18 ]ความยากลำบากในการย้ายฟังก์ชันเหล่านี้ไปยังโมดูลเสมือน SYSTEM คือการขาดตัวระบุสำหรับฟังก์ชันที่จะนำเข้าได้ เนื่องจากสามารถนำเข้าได้เฉพาะตัวระบุเท่านั้น แต่ไม่สามารถนำเข้าไวยากรณ์ได้
ระบบนำเข้า; (* อนุญาตให้ใช้สิ่งอำนวยความสะดวกที่ไม่ปลอดภัยบางอย่าง: *) ตัวแปรword : SYSTEM . WORD ; addr : SYSTEM . ADDRESS ; addr := SYSTEM . ADR ( word );(*แต่สามารถใช้ไวยากรณ์การแปลงประเภทได้โดยไม่ต้องนำเข้าเช่นนั้น*) VAR i : INTEGER ; n : CARDINAL ; n := CARDINAL ( i ); (* หรือ *) i := INTEGER ( n );มาตรฐาน ISO Modula-2 ได้แก้ไขปัญหานี้สำหรับฟังก์ชันการแปลงประเภทโดยการเปลี่ยนไวยากรณ์การแปลงประเภทเป็นฟังก์ชันที่เรียกว่า CAST ซึ่งต้องนำเข้าจากโมดูลเสมือน SYSTEM อย่างไรก็ตาม ฟังก์ชันที่ไม่ปลอดภัยอื่นๆ เช่น บันทึกตัวแปรยังคงใช้งานได้โดยไม่ต้องนำเข้าจากโมดูลเสมือน SYSTEM [ 19 ]
นำเข้าSYSTEM ; VAR i : INTEGER ; n : CARDINAL ; i := SYSTEM.CAST ( INTEGER , n ); (* แปลง ประเภท เป็นISO Modula-2 *)การแก้ไขภาษาครั้งล่าสุดได้นำปรัชญาการออกแบบดั้งเดิมมาใช้อย่างเคร่งครัด ขั้นแรก โมดูลเสมือน SYSTEM ถูกเปลี่ยนชื่อเป็น UNSAFE เพื่อให้ลักษณะที่ไม่ปลอดภัยของสิ่งอำนวยความสะดวกที่นำเข้าจากที่นั่นมีความชัดเจนมากขึ้น จากนั้นสิ่งอำนวยความสะดวกที่ไม่ปลอดภัยที่เหลือทั้งหมดจะถูกลบออกทั้งหมด (เช่น บันทึกตัวแปร) หรือย้ายไปยังโมดูลเสมือน UNSAFE สำหรับสิ่งอำนวยความสะดวกที่ไม่มีตัวระบุที่สามารถนำเข้าได้ จะมีการแนะนำตัวระบุการเปิดใช้งาน เพื่อเปิดใช้งานสิ่งอำนวยความสะดวกดังกล่าว จะต้องนำเข้าตัวระบุการเปิดใช้งานที่เกี่ยวข้องจากโมดูลเสมือน UNSAFE ไม่มีสิ่งอำนวยความสะดวกที่ไม่ปลอดภัยใด ๆ เหลืออยู่ในภาษาที่ไม่ต้องนำเข้าจาก UNSAFE [ 18 ]
นำเข้าUNSAFE ; ตัวแปรi : INTEGER ; n : CARDINAL ; i := UNSAFE.CAST ( INTEGER , n ) ; (* การแปลงประเภทใน Modula-2 ฉบับแก้ไขปี 2010 *)FROM UNSAFE IMPORT FFI ; (* ตัวระบุสำหรับอินเทอร์เฟซฟังก์ชันภายนอก *) <*FFI="C"*> (* คำสั่งเฉพาะสำหรับอินเทอร์เฟซฟังก์ชันภายนอกไปยัง C *)ปาสคาล
ภาษา Pascalมีข้อกำหนดด้านความปลอดภัยของชนิดข้อมูลอยู่หลายประการ ซึ่งบางส่วนยังคงอยู่ในคอมไพเลอร์บางตัว ในกรณีที่คอมไพเลอร์ Pascal กำหนดให้ใช้ "การกำหนดชนิดข้อมูลอย่างเข้มงวด" ตัวแปรสองตัวจะไม่สามารถกำหนดค่าให้กันได้ เว้นแต่ว่าตัวแปรทั้งสองจะเข้ากันได้ (เช่น การแปลงจำนวนเต็มเป็นจำนวนจริง) หรือถูกกำหนดให้มีชนิดย่อยเดียวกัน ตัวอย่างเช่น หากคุณมีส่วนของโค้ดต่อไปนี้:
type TwoTypes = record I : Integer ; Q : Real ; end ;DualTypes = record I : Integer ; Q : Real ; end ;var T1 , T2 : TwoTypes ; D1 , D2 : DualTypes ;ภายใต้กฎการกำหนดประเภทที่เข้มงวด ตัวแปรที่กำหนดเป็นTwoTypesจะไม่เข้ากันกับDualTypes (เนื่องจากไม่เหมือนกัน แม้ว่าส่วนประกอบของประเภทที่ผู้ใช้กำหนดนั้นจะเหมือนกันก็ตาม) และการกำหนดค่าให้กับ ตัวแปรนั้น จึงผิดกฎหมาย การกำหนดค่าให้กับ ตัวแปรนั้น จะถูกต้องตามกฎหมาย เนื่องจากชนิดย่อยที่กำหนดไว้นั้นเหมือนกัน อย่างไรก็ตาม การกำหนดค่าเช่นนั้นจะถูกต้องตามกฎหมาย T1 := D2;T1 := T2;T1.Q := D1.Q;
ลิสปาร์กทั่วไป
โดยทั่วไปCommon Lispเป็นภาษาที่มีความปลอดภัยด้านประเภท คอมไพเลอร์ Common Lisp มีหน้าที่ในการแทรกการตรวจสอบแบบไดนามิกสำหรับการดำเนินการที่ไม่สามารถพิสูจน์ความปลอดภัยของประเภทได้แบบคงที่ อย่างไรก็ตาม โปรแกรมเมอร์อาจระบุว่าโปรแกรมควรได้รับการคอมไพล์ด้วยการตรวจสอบประเภทแบบไดนามิกในระดับที่ต่ำกว่า[ 20 ]โปรแกรมที่คอมไพล์ในโหมดดังกล่าวไม่สามารถถือว่ามีความปลอดภัยด้านประเภทได้
ตัวอย่างภาษา C++
ตัวอย่างต่อไปนี้แสดงให้เห็นว่าตัวดำเนินการแปลงชนิดข้อมูล (cast operator) ใน C++ อาจทำให้ความปลอดภัยของชนิดข้อมูลเสียหายได้หากใช้งานไม่ถูกต้อง ตัวอย่างแรกแสดงให้เห็นว่าชนิดข้อมูลพื้นฐานอาจถูกแปลงชนิดข้อมูลอย่างไม่ถูกต้องได้อย่างไร:
#include <iostream> using namespace std ;int main () { int ival = 5 ; // ค่าจำนวนเต็มfloat fval = reinterpret_cast < float &> ( ival ); // แปลงรูปแบบบิตcout << fval << endl ; // แสดงผลจำนวนเต็มเป็น float return 0 ; }ในตัวอย่างนี้reinterpret_castป้องกันไม่ให้คอมไพเลอร์ทำการแปลงค่าจำนวนเต็มเป็นค่าทศนิยมอย่างปลอดภัยโดยชัดเจน[ 21 ]เมื่อโปรแกรมทำงาน มันจะส่งออกค่าทศนิยมที่ไม่ถูกต้อง ปัญหานี้สามารถหลีกเลี่ยงได้โดยการเขียนแทนfloat fval = ival;
ตัวอย่างต่อไปนี้แสดงให้เห็นว่าการอ้างอิงวัตถุอาจถูกแปลงประเภทลงอย่างไม่ถูกต้องได้อย่างไร:
#include <iostream> using namespace std ;class Parent { public : virtual ~ Parent () {} // ตัวทำลายเสมือนสำหรับ RTTI };คลาสChild1 : public Parent { public : int a ; };คลาสChild2 : public Parent { public : float b ; };int main () { Child1 c1 ; c1 . a = 5 ; Parent & p = c1 ; // การแปลงขึ้น (upcast) ปลอดภัยเสมอChild2 & c2 = static_cast < Child2 &> ( p ); // การแปลงลง (downcast) ไม่ถูกต้องcout << c2 . b << endl ; // จะแสดงข้อมูลที่ไม่ถูกต้องreturn 0 ; }คลาสย่อยทั้งสองมีสมาชิกประเภทต่างกัน เมื่อแปลงพอยเตอร์ของคลาสแม่เป็นพอยเตอร์ของคลาสลูก พอยเตอร์ที่ได้อาจไม่ได้ชี้ไปยังวัตถุที่ถูกต้องตามประเภทที่ถูกต้อง ในตัวอย่างนี้ จะทำให้พิมพ์ค่าขยะออกมา ปัญหานี้สามารถหลีกเลี่ยงได้โดยการแทนที่static_castด้วยdynamic_castสิ่งที่โยนข้อยกเว้นเมื่อมีการแปลงที่ไม่ถูกต้อง[ 22 ]
ดูเพิ่มเติม
หมายเหตุ
- ^ "สิ่งที่ควรรู้ก่อนถกเถียงเรื่องระบบประเภทข้อมูล | Ovid [blogs.perl.org]" . blogs.perl.org . สืบค้นเมื่อ2023-06-27 .
- ^ a b Milner, Robin (1978), "ทฤษฎีของโพลีมอร์ฟิซึมประเภทในการเขียนโปรแกรม", Journal of Computer and System Sciences , 17 (3): 348– 375, doi : 10.1016/0022-0000(78)90014-4 , hdl : 20.500.11820/d16745d7-f113-44f0-a7a3-687c2b709f66
- ^ a b Saraswat, Vijay (15 สิงหาคม 1997). "Java ไม่ปลอดภัยต่อประเภทข้อมูล" . สืบค้นเมื่อ8 ตุลาคม 2008 .
- ^ Wright, AK; Felleisen, M. (15 พฤศจิกายน 1994). "แนวทางเชิงไวยากรณ์สู่ความถูกต้องของประเภท" . ข้อมูลและการคำนวณ . 115 (1): 38– 94. doi : 10.1006/inco.1994.1093 . ISSN 0890-5401 .
- ^ Damas, Luis; Milner, Robin (25 มกราคม 1982). "Principal type-schemes for functional programs" . Proceedings of the 9th ACM SIGPLAN-SIGACT symposium on Principles of programming languages - POPL '82 . Association for Computing Machinery. pp. 207– 212. doi : 10.1145/582153.582176 . ISBN 0897910656. S2CID 11319320 .
- ^ Tofte, Mads (1988). ความหมายเชิงปฏิบัติการและการอนุมานประเภทพหุรูป (วิทยานิพนธ์)
- ^ Henriksen, Troels; Elsman, Martin (17 มิถุนายน 2021). "มุ่งสู่ประเภทที่ขึ้นอยู่กับขนาดสำหรับการเขียนโปรแกรมอาร์เรย์" Proceedings of the 7th ACM SIGPLAN International Workshop on Libraries, Languages and Compilers for Array Programming . Association for Computing Machinery. หน้า 1–14 . doi : 10.1145/3460944.3464310 . ISBN 9781450384667. S2CID 235474098 .
- ^ Standard ML . Smlnj.org. สืบค้นเมื่อ 2 พฤศจิกายน 2013
- ^ "System.IO.Unsafe" . คู่มือไลบรารี GHC: base-3.0.1.0 . เก็บถาวรจากต้นฉบับเมื่อ 2008-07-05 . เรียกดูเมื่อ2008-07-17 .
- ^ Liskov, B; Zilles, S (1974). "การเขียนโปรแกรมด้วยชนิดข้อมูลนามธรรม". ACM SIGPLAN Notices . 9 (4): 50– 59. CiteSeerX 10.1.1.136.3043 . doi : 10.1145/942572.807045 .
- ^แจ็คสัน, เค. (1977). "การประมวลผลแบบขนานและการสร้างซอฟต์แวร์แบบโมดูลาร์" การออกแบบและการใช้งานภาษาโปรแกรม . บันทึกการบรรยายในวิทยาการคอมพิวเตอร์. เล่มที่ 54. หน้า 436–443 . doi : 10.1007/BFb0021435 . ISBN 3-540-08360-X.
- ^ "CS1130. การเปลี่ยนผ่านสู่การเขียนโปรแกรมเชิงวัตถุ – ภาคเรียนฤดูใบไม้ผลิ 2012 – เวอร์ชันเรียนรู้ด้วยตนเอง" . มหาวิทยาลัยคอร์เนล, ภาควิชาวิทยาการคอมพิวเตอร์. 2005. สืบค้นเมื่อ2023-09-15 .
- ^ Pierce, Benjamin C. (2002). ประเภทและภาษาโปรแกรม . เคมบริดจ์ แมสซาชูเซตส์: สำนักพิมพ์ MIT. หน้า 158. ISBN 0-262-16209-1.
- ^ความปลอดภัยของประเภทข้อมูลจึงเป็นเรื่องของการกำหนดคลาสที่ดีด้วยเช่นกัน: เมธอดสาธารณะที่แก้ไขสถานะภายในของวัตถุจะต้องรักษาความสมบูรณ์ของวัตถุนั้นไว้
- ^ Kernighan ; Dennis M. Ritchie (มีนาคม 1988). ภาษาการเขียนโปรแกรม C (ฉบับที่ 2). Englewood Cliffs, NJ : Prentice Hall . หน้า 142 . ISBN 978-0-13-110362-7ใน ภาษา
C วิธีที่ถูกต้องคือการประกาศฟังก์ชัน
mallocที่ส่งคืนพอยเตอร์ไปยังvoidจากนั้นแปลงพอยเตอร์นั้นให้เป็นชนิดที่ต้องการอย่างชัดเจนด้วยการแปลงชนิดข้อมูล (cast) - ^ "ศูนย์ความรู้ของ IBM" . ibm.com .
- ↑นิเคลาส์ เวิร์ธ (1985) การเขียนโปรแกรมใน Modula- 2 สปริงเกอร์ แวร์แล็ก.
- ^ a b "การแยกสถานที่ที่ปลอดภัยและไม่ปลอดภัย"สืบค้นเมื่อ24 มีนาคม 2558
- ↑ "การอ้างอิงภาษา ISO Modula-2 " สืบค้นเมื่อ24 มีนาคม 2558 .
- ^ "Common Lisp HyperSpec" . สืบค้นเมื่อ26 พฤษภาคม 2013 .
- ^ "reinterpret_cast conversion - cppreference.com" . En.cppreference.com . สืบค้นเมื่อ2022-09-21 .
- ^ "การแปลง dynamic_cast - cppreference.com" . En.cppreference.com . สืบค้นเมื่อ2022-09-21 .
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ ประเภทความปลอดภัย
ใน วิทยาการคอมพิวเตอร์ ความ ปลอดภัยของชนิดข้อมูล หมายถึง ระดับที่ ภาษาโปรแกรมนั้นๆ ลดหรือป้องกัน ข้อผิดพลาดเกี่ยวกับชนิดข้อมูล ภาษาที่มีความปลอดภัยของชนิดข้อมูลสูงบางครั้งเรียกว่า...
คำจำกัดความ
โดยสัญชาตญาณแล้ว ความถูกต้องของรูปแบบตัวอักษรนั้นสามารถอธิบายได้ด้วยคำกล่าวที่คมคายของ Robin Milner ที่ว่า
ความสัมพันธ์กับความปลอดภัยรูปแบบอื่นๆ
โดยลำพังแล้ว ความถูกต้องของประเภทข้อมูลเป็นคุณสมบัติที่ค่อนข้างอ่อนแอ เนื่องจากโดยพื้นฐานแล้วมันเพียงแค่ระบุว่ากฎของระบบประเภทข้อมูลนั้นมีความสอดคล้องกันภายในและไม่สามารถถูกบิดเบือนได้ อย่างไรก็ตาม ในทางปฏิบัติ...
ภาษาที่มีประเภทข้อมูลปลอดภัยและภาษาที่มีประเภทข้อมูลไม่ปลอดภัย
ความปลอดภัยของประเภทมักเป็นข้อกำหนดสำหรับภาษาของเล่นใดๆ (เช่น ภาษาเฉพาะกลุ่ม ) ที่เสนอในการวิจัยภาษาโปรแกรมเชิงวิชาการ อย่างไรก็ตาม ภาษาหลายภาษามีขนาดใหญ่เกินกว่าจะพิสูจน์ความปลอดภัยของประเภทที่สร้างโดยมนุษย์ได้ เนื่องจากมักจะต้องตรวจสอบหลายพันกรณี ถึงกระนั้น...