เหตุผลที่แท้จริงเกิดขึ้นจากความแตกต่างพื้นฐานในเจตนาระหว่าง C และ C ++ ในมือข้างหนึ่งและ Java และ C # (สำหรับตัวอย่างเพียงสองสาม) ที่อื่น ๆ ด้วยเหตุผลทางประวัติศาสตร์การสนทนาส่วนใหญ่ที่นี่พูดถึง C มากกว่า C ++ แต่ (อย่างที่คุณอาจทราบแล้ว) C ++ เป็นผู้สืบทอดโดยตรงของ C ดังนั้นสิ่งที่บอกว่าเกี่ยวกับ C ใช้กับ C ++ อย่างเท่าเทียมกัน
แม้ว่าพวกเขาจะลืมไปแล้ว (และบางครั้งการดำรงอยู่ของพวกเขาก็ถูกปฏิเสธ) แต่รุ่นแรก ๆ ของ UNIX ถูกเขียนด้วยภาษาแอสเซมบลี จุดประสงค์ดั้งเดิมของ C ส่วนใหญ่ (ถ้าไม่ใช่เพียงอย่างเดียว) คือพอร์ต UNIX จากภาษาแอสเซมบลีไปยังภาษาระดับสูงกว่า จุดประสงค์ส่วนหนึ่งคือการเขียนระบบปฏิบัติการให้มากที่สุดเท่าที่จะเป็นไปได้ในภาษาระดับที่สูงขึ้น - หรือมองจากอีกด้านหนึ่งเพื่อลดจำนวนที่ต้องเขียนด้วยภาษาแอสเซมบลี
เพื่อให้บรรลุผลนั้น C จำเป็นต้องให้การเข้าถึงฮาร์ดแวร์ในระดับใกล้เคียงกับภาษาแอสเซมบลี PDP-11 (ตัวอย่างหนึ่ง) แมป I / O ที่ลงทะเบียนไปยังที่อยู่ที่เฉพาะเจาะจง ตัวอย่างเช่นคุณจะอ่านตำแหน่งหน่วยความจำหนึ่งแห่งเพื่อตรวจสอบว่ามีการกดคีย์บนคอนโซลระบบหรือไม่ มีการตั้งค่าบิตหนึ่งในตำแหน่งนั้นเมื่อมีข้อมูลรอการอ่าน จากนั้นคุณจะอ่านไบต์จากตำแหน่งอื่นที่ระบุเพื่อดึงรหัส ASCII ของคีย์ที่ถูกกด
ในทำนองเดียวกันหากคุณต้องการพิมพ์ข้อมูลบางอย่างคุณจะต้องตรวจสอบตำแหน่งอื่นที่ระบุและเมื่ออุปกรณ์เอาต์พุตพร้อมคุณจะเขียนข้อมูลของคุณอีกตำแหน่งที่ระบุ
เพื่อรองรับการเขียนไดรเวอร์สำหรับอุปกรณ์ดังกล่าว C อนุญาตให้คุณระบุตำแหน่งที่กำหนดเองโดยใช้ชนิดจำนวนเต็มแปลงเป็นตัวชี้และอ่านหรือเขียนตำแหน่งนั้นในหน่วยความจำ
แน่นอนว่านี่เป็นปัญหาที่ค่อนข้างร้ายแรง: ไม่ใช่ว่าทุกเครื่องในโลกจะมีหน่วยความจำวางเหมือนกับ PDP-11 ตั้งแต่ต้นปี 1970 ดังนั้นเมื่อคุณใช้จำนวนเต็มนั้นให้แปลงเป็นตัวชี้แล้วอ่านหรือเขียนผ่านตัวชี้นั้นไม่มีใครสามารถรับประกันได้อย่างสมเหตุสมผลเกี่ยวกับสิ่งที่คุณจะได้รับ สำหรับตัวอย่างที่ชัดเจนการอ่านและการเขียนอาจแมปเพื่อแยกการลงทะเบียนในฮาร์ดแวร์ดังนั้นคุณ (ตรงกันข้ามกับหน่วยความจำปกติ) ถ้าคุณเขียนอะไรแล้วลองอ่านกลับสิ่งที่คุณอ่านอาจไม่ตรงกับสิ่งที่คุณเขียน
ฉันเห็นความเป็นไปได้สองสามอย่างที่เหลืออยู่:
- กำหนดอินเทอร์เฟซให้กับฮาร์ดแวร์ที่เป็นไปได้ทั้งหมด - ระบุที่อยู่ที่แน่นอนของสถานที่ทั้งหมดที่คุณอาจต้องการอ่านหรือเขียนเพื่อโต้ตอบกับฮาร์ดแวร์ในทางใดทางหนึ่ง
- ห้ามการเข้าถึงในระดับนั้นและประกาศว่าใครก็ตามที่ต้องการทำสิ่งนั้นจำเป็นต้องใช้ภาษาแอสเซมบลี
- อนุญาตให้ผู้อื่นทำเช่นนั้น แต่ปล่อยให้พวกเขาอ่าน (เช่น) คู่มือสำหรับฮาร์ดแวร์ที่พวกเขากำลังกำหนดเป้าหมายและเขียนรหัสเพื่อให้พอดีกับฮาร์ดแวร์ที่พวกเขากำลังใช้
ในบรรดาสิ่งเหล่านี้ 1 ดูเหมือนว่าผิดปกติมากพอที่จะไม่คุ้มค่ากับการอภิปรายเพิ่มเติม 2 เป็นการทิ้งความตั้งใจพื้นฐานของภาษาออกไป นั่นทำให้ตัวเลือกที่สามเป็นหลักอย่างเดียวเท่านั้นที่พวกเขาสามารถพิจารณาได้อย่างสมเหตุสมผล
จุดอื่นที่เกิดขึ้นค่อนข้างบ่อยคือขนาดของประเภทจำนวนเต็ม C รับตำแหน่ง "" ที่int
ควรเป็นขนาดธรรมชาติที่แนะนำโดยสถาปัตยกรรม ดังนั้นถ้าฉันเขียนโปรแกรม VAX แบบint
32 บิตน่าจะเป็น 32 บิต แต่ถ้าฉันเขียนโปรแกรม Univac แบบint
36 บิตน่าจะเป็น 36 บิต (เป็นต้น) อาจไม่สมเหตุสมผล (และอาจไม่สามารถทำได้) ในการเขียนระบบปฏิบัติการสำหรับคอมพิวเตอร์ 36 บิตโดยใช้ชนิดเท่านั้นที่รับประกันว่าจะมีขนาดทวีคูณ 8 บิต บางทีฉันอาจเป็นเพียงผิวเผิน แต่ดูเหมือนว่าถ้าฉันกำลังเขียนระบบปฏิบัติการสำหรับเครื่อง 36 บิตฉันอาจต้องการใช้ภาษาที่รองรับชนิด 36 บิต
จากมุมมองของภาษาสิ่งนี้นำไปสู่พฤติกรรมที่ไม่ได้กำหนดเพิ่มเติม ถ้าฉันใช้ค่ามากที่สุดที่พอดีกับ 32 บิตจะเกิดอะไรขึ้นเมื่อฉันเพิ่ม 1 สำหรับฮาร์ดแวร์แบบ 32 บิตโดยทั่วไปมันจะพลิก (หรืออาจจะเป็นความผิดพลาดของฮาร์ดแวร์บางอย่าง) ในทางตรงกันข้ามถ้ามันทำงานบนฮาร์ดแวร์ 36 บิตมันจะ ... เพิ่มอีกอัน หากภาษากำลังจะสนับสนุนการเขียนระบบปฏิบัติการคุณไม่สามารถรับประกันได้ว่าพฤติกรรมใด - คุณต้องยอมให้ทั้งขนาดของประเภทและพฤติกรรมของการไหลล้นแตกต่างกันไปในแต่ละแบบ
Java และ C # สามารถเพิกเฉยได้ทั้งหมด พวกเขาไม่ได้มีวัตถุประสงค์เพื่อสนับสนุนการเขียนระบบปฏิบัติการ คุณมีทางเลือกสองทาง หนึ่งคือการทำให้ฮาร์ดแวร์สนับสนุนสิ่งที่พวกเขาต้องการ - เนื่องจากพวกเขาต้องการประเภทที่ 8, 16, 32 และ 64 บิตเพียงแค่สร้างฮาร์ดแวร์ที่รองรับขนาดเหล่านั้น ความเป็นไปได้อื่น ๆ ที่ชัดเจนคือภาษานั้นจะทำงานบนซอฟต์แวร์อื่นที่มีสภาพแวดล้อมที่พวกเขาต้องการโดยไม่คำนึงถึงสิ่งที่ฮาร์ดแวร์พื้นฐานต้องการ
ในกรณีส่วนใหญ่นี่ไม่ใช่ตัวเลือก / หรือตัวเลือกจริงๆ แต่การใช้งานจำนวนมากทำทั้งสองอย่างเล็กน้อย ปกติแล้วคุณจะรัน Java บน JVM ที่รันบนระบบปฏิบัติการ บ่อยครั้งที่ระบบปฏิบัติการเขียนใน C และ JVM ใน C ++ หาก JVM ทำงานบนซีพียู ARM มีโอกาสค่อนข้างดีที่ซีพียูจะรวมส่วนขยาย Jazelle ของ ARM เพื่อปรับแต่งฮาร์ดแวร์ให้ใกล้เคียงกับความต้องการของจาวามากขึ้นดังนั้นความต้องการซอฟต์แวร์ในจาวาน้อยลงและโค้ดจาวาทำงานได้เร็วขึ้น ช้าๆ)
สรุป
C และ C ++ มีพฤติกรรมที่ไม่ได้กำหนดเนื่องจากไม่มีใครกำหนดทางเลือกที่ยอมรับได้ซึ่งอนุญาตให้พวกเขาทำสิ่งที่พวกเขาตั้งใจจะทำ C # และ Java ใช้แนวทางที่แตกต่างกัน แต่วิธีการนั้นเหมาะสมไม่ดี (ถ้าเลย) กับเป้าหมายของ C และ C ++ โดยเฉพาะอย่างยิ่งดูเหมือนจะไม่มีวิธีที่เหมาะสมในการเขียนซอฟต์แวร์ระบบ (เช่นระบบปฏิบัติการ) บนฮาร์ดแวร์ที่เลือกโดยพลการส่วนใหญ่ ทั้งสองมักจะขึ้นอยู่กับสิ่งอำนวยความสะดวกที่มีให้โดยซอฟต์แวร์ระบบที่มีอยู่ (มักเขียนใน C หรือ C ++) เพื่อทำงานของพวกเขา