ในอดีต (บางทีโดยการเขียนบางส่วนของมัน) มันเป็นสิ่งที่ตรงกันข้าม ในคอมพิวเตอร์เครื่องแรกของต้นปี 1970 (อาจเป็นPDP-11 ) ที่ใช้ตัวอ่อนต้นแบบ C (บางทีBCPL ) ไม่มีMMUและไม่มีการป้องกันหน่วยความจำ (ซึ่งมีอยู่บนเมนเฟรมของIBM / 360 ที่เก่าที่สุด) ดังนั้นหน่วยความจำทุกไบต์ (รวมถึงการจัดการสตริงตัวอักษรหรือรหัสเครื่อง) อาจถูกเขียนทับโดยโปรแกรมที่ผิดพลาด (ลองนึกภาพโปรแกรมที่เปลี่ยนบางอย่าง%
ไป/
ในprintf (3)สตริงรูปแบบ) ดังนั้นสายอักขระและค่าคงที่จึงเขียนได้
เมื่อเป็นวัยรุ่นในปี 1975 ฉันเขียนโค้ดในพิพิธภัณฑ์ Palais de la Découverteในกรุงปารีสโดยใช้คอมพิวเตอร์ยุค 60 ปีโดยไม่มีการป้องกันหน่วยความจำ: IBM / 1620มีหน่วยความจำหลักเท่านั้น - ซึ่งคุณสามารถเริ่มต้นผ่านแป้นพิมพ์ได้หลายสิบ ของตัวเลขเพื่ออ่านโปรแกรมเริ่มต้นบนเทปเจาะ; CAB / 500มีหน่วยความจำดรัมแม่เหล็ก; คุณสามารถปิดการเขียนแทร็กผ่านสวิตช์เชิงกลใกล้กับดรัม
ต่อมาคอมพิวเตอร์ได้รับรูปแบบของหน่วยความจำการจัดการหน่วย (MMU) ที่มีการป้องกันหน่วยความจำบางส่วน มีอุปกรณ์ที่ห้ามไม่ให้ซีพียูเขียนทับหน่วยความจำบางชนิด ดังนั้นหน่วยความจำบางเซ็กเมนต์สะดุดตาเซ็กเมนต์รหัส ( เซ็กเมนต์ aka .text
) กลายเป็นแบบอ่านอย่างเดียว (ยกเว้นโดยระบบปฏิบัติการที่โหลดจากดิสก์) มันเป็นเรื่องธรรมดาสำหรับคอมไพเลอร์และตัวเชื่อมโยงที่จะนำสตริงตัวอักษรในส่วนรหัสนั้นและสตริงตัวอักษรกลายเป็นอ่านอย่างเดียว เมื่อโปรแกรมของคุณพยายามที่จะเขียนทับพวกเขาก็ไม่เป็นผลดีเป็นพฤติกรรมที่ไม่ได้กำหนด และการมีเซ็กเมนต์โค้ดแบบอ่านอย่างเดียวในหน่วยความจำเสมือนให้ประโยชน์ที่สำคัญ: กระบวนการหลายอย่างที่รันโปรแกรมเดียวกันใช้RAMเดียวกัน( หน่วยความจำกายภาพ)หน้า) สำหรับส่วนรหัสนั้น (ดูที่การMAP_SHARED
ตั้งค่าสถานะสำหรับmmap (2)บน Linux)
วันนี้ไมโครคอนโทรลเลอร์ราคาถูกมีหน่วยความจำแบบอ่านอย่างเดียว (เช่นแฟลชหรือ ROM) และเก็บรหัสไว้ (และสตริงตัวอักษรและค่าคงที่อื่น ๆ ) ที่นั่น และไมโครโปรเซสเซอร์จริง (เช่นในแท็บเล็ตแล็ปท็อปหรือเดสก์ทอป) มีหน่วยจัดการหน่วยความจำที่มีความซับซ้อนและแคชเครื่องจักรที่ใช้สำหรับหน่วยความจำเสมือนและเพจ ดังนั้นส่วนของรหัสของปฏิบัติการโปรแกรม (เช่นในเอลฟ์ ) เป็นหน่วยความจำที่แมปเป็นแบบอ่านอย่างเดียวที่สามารถแชร์และส่วนปฏิบัติการ (โดยmmap (2)หรือexecve (2)บน Linux; BTW คุณสามารถให้คำแนะนำแก่LDเพื่อรับเซ็กเมนต์รหัสที่เขียนได้หากคุณต้องการ) การเขียนหรือการเหยียดหยามมันโดยทั่วไปเป็นความผิดส่วน
ดังนั้นมาตรฐาน C คือบาร็อค: ถูกกฎหมาย (สำหรับเหตุผลทางประวัติศาสตร์เท่านั้น) สตริงตัวอักษรไม่ใช่const char[]
อาร์เรย์ แต่มีเฉพาะchar[]
อาร์เรย์ที่ถูกห้ามไม่ให้เขียนทับ
BTW บางภาษาปัจจุบันอนุญาตให้มีการเขียนตัวอักษรสตริง (แม้ Ocaml ซึ่งในอดีต - และไม่ดี - มีสตริงตัวอักษรที่เขียนได้เปลี่ยนพฤติกรรมนั้นเมื่อเร็ว ๆ นี้ใน 4.02 และตอนนี้มีสตริงแบบอ่านอย่างเดียว)
คอมไพเลอร์ C ปัจจุบันสามารถเพิ่มประสิทธิภาพและมี"ions"
และ"expressions"
แบ่งปัน 5 ไบต์สุดท้ายของพวกเขา (รวมถึงการยกเลิก null null)
ลองรวบรวมรหัส C ของคุณในไฟล์foo.c
ด้วยgcc -O -fverbose-asm -S foo.c
และดูข้างในไฟล์แอสเซมเบลอร์ที่สร้างfoo.s
โดยGCC
ในที่สุดความหมายของ C นั้นซับซ้อนเพียงพอ (อ่านเพิ่มเติมเกี่ยวกับCompCert & Frama-Cซึ่งพยายามจับภาพ) และการเพิ่มสตริงตัวอักษรคงที่ที่เขียนได้จะทำให้มีความลับมากขึ้นในขณะที่ทำให้โปรแกรมอ่อนลงและปลอดภัยน้อยลง พฤติกรรมที่กำหนดไว้) ดังนั้นจึงไม่น่าเป็นไปได้มากที่มาตรฐาน C ในอนาคตจะยอมรับสตริงตัวอักษรที่เขียนได้ บางทีในทางตรงกันข้ามพวกเขาจะทำให้พวกเขาconst char[]
อาร์เรย์ตามที่ควรจะเป็นทางศีลธรรม
โปรดสังเกตด้วยว่าด้วยเหตุผลหลายประการข้อมูลที่ไม่แน่นอนที่จะจัดการได้โดยคอมพิวเตอร์ (แคชเชื่อมโยงกัน) รหัสสำหรับทำความเข้าใจโดยนักพัฒนามากกว่าข้อมูลคงที่ ดังนั้นจึงเป็นที่นิยมในการได้มากที่สุดของข้อมูลของคุณ (และสตริงตัวอักษรสะดุดตา) เข้าพักไม่เปลี่ยนรูป อ่านเพิ่มเติมเกี่ยวกับกระบวนทัศน์การเขียนโปรแกรมใช้งาน ได้
ใน Fortran77 วันเก่าบน IBM / 7094 ข้อผิดพลาดอาจเปลี่ยนค่าคงที่: ถ้าคุณCALL FOO(1)
และถ้าFOO
เกิดขึ้นเพื่อแก้ไขอาร์กิวเมนต์ที่ส่งผ่านโดยอ้างอิงถึง 2 การใช้งานอาจมีการเปลี่ยนแปลงเกิดขึ้นเป็น 1 เป็น 2 และนั่นเป็นเรื่องจริง ข้อผิดพลาดที่ค่อนข้างหายาก