ในการใช้งานกับโมเดลหน่วยความจำแบบแบน (โดยพื้นฐานแล้วทุกอย่าง) การส่งไปที่uintptr_t
Just Work
(แต่ดูการเปรียบเทียบตัวชี้ควรลงนามหรือไม่ได้ลงนามใน 64- บิต x86 หรือไม่สำหรับการอภิปรายว่าคุณควรปฏิบัติต่อพอยน์เตอร์ตามที่ลงชื่อหรือไม่รวมถึงปัญหาในการสร้างพอยน์เตอร์นอกวัตถุซึ่งเป็น UB ใน C. )
แต่ระบบที่มีรุ่นหน่วยความจำไม่แบนทำอยู่และคิดเกี่ยวกับพวกเขาสามารถช่วยอธิบายสถานการณ์ปัจจุบันเช่น C ++ มีรายละเอียดที่แตกต่างกันสำหรับเทียบกับ<
std::less
ส่วนหนึ่งของจุด<
บนตัวชี้ไปยังวัตถุที่แยกเป็น UB ใน C (หรืออย่างน้อยไม่ได้ระบุในการแก้ไข C ++ บางส่วน) คือการอนุญาตให้เครื่องแปลก ๆ รวมถึงรุ่นหน่วยความจำที่ไม่ใช่แบน
ตัวอย่างที่รู้จักกันดีคือโหมดจริง x86-16 ที่ชี้เป็นส่วน: ชดเชยในรูปแบบ 20 (segment << 4) + offset
บิตเชิงเส้นที่อยู่ผ่านทาง ที่อยู่เชิงเส้นเดียวกันสามารถแสดงโดยชุดค่าผสม seg: off แบบหลายค่าได้
C ++ std::less
สำหรับพอยน์เตอร์บน ISAs แปลก ๆ อาจต้องมีราคาแพงเช่น "ปกติ" เซกเมนต์: ออฟเซ็ตบน x86-16 เพื่อออฟเซ็ต <= 15 อย่างไรก็ตามไม่มีวิธีพกพาในการติดตั้ง การจัดการที่จำเป็นในการทำให้เป็นปกติuintptr_t
(หรือการแทนวัตถุของวัตถุตัวชี้) เป็นการใช้งานเฉพาะ
แต่ถึงแม้ในระบบที่ C ++ std::less
ต้องมีราคาแพง<
ก็ไม่จำเป็นต้องเป็น ตัวอย่างเช่นสมมติว่าโมเดลหน่วยความจำ "ใหญ่" ที่วัตถุควรอยู่ในส่วนเดียว<
สามารถเปรียบเทียบส่วนออฟเซ็ตและไม่ต้องกังวลกับส่วนเซ็กเมนต์ (ตัวชี้ภายในวัตถุเดียวกันจะมีเซ็กเมนต์เดียวกันและมิฉะนั้นจะเป็น UB ใน C. C ++ 17 เปลี่ยนเป็น "ไม่ระบุ" เท่านั้นซึ่งอาจอนุญาตให้ข้ามการทำให้เป็นมาตรฐานและเปรียบเทียบการออฟเซ็ตได้) นี่เป็นการสมมติพอยน์เตอร์ทุกส่วน ของวัตถุจะใช้seg
ค่าเดียวกันเสมอไม่ทำให้ปกติ นี่คือสิ่งที่คุณคาดหวังให้ ABI ต้องการสำหรับ "ใหญ่" ซึ่งตรงข้ามกับโมเดลหน่วยความจำ "ใหญ่" (ดูการอภิปรายในความคิดเห็น )
(โมเดลหน่วยความจำดังกล่าวอาจมีขนาดวัตถุสูงสุด 64kiB แต่พื้นที่ที่อยู่รวมสูงสุดที่ใหญ่กว่าซึ่งมีที่ว่างสำหรับวัตถุขนาดใหญ่ที่สุดเช่นนี้ ISO C ช่วยให้การใช้งานมีขีด จำกัด ของขนาดวัตถุที่ต่ำกว่า ค่าสูงสุด (ไม่ได้ลงนาม) size_t
สามารถเป็นตัวแทนSIZE_MAX
ได้ตัวอย่างเช่นแม้ในระบบแบบจำลองหน่วยความจำแบบแบน GNU C จำกัด ขนาดของวัตถุให้ใหญ่สุดเพื่อPTRDIFF_MAX
ให้การคำนวณขนาดสามารถเพิกเฉยต่อการโอเวอร์โฟลว์ที่ลงชื่อแล้ว) ดูคำตอบและการอภิปราย
หากคุณต้องการอนุญาตให้วัตถุมีขนาดใหญ่กว่าเซกเมนต์คุณต้องมีโมเดลหน่วยความจำ "ใหญ่" ที่ต้องกังวลเกี่ยวกับการโอเวอร์โฟลว์ส่วนออฟเซ็ตของตัวชี้เมื่อทำการp++
วนลูปผ่านอาร์เรย์หรือเมื่อทำการคำนวณดัชนี / ตัวชี้ทางคณิตศาสตร์ สิ่งนี้นำไปสู่โค้ดที่ช้าลงทุกที่ แต่อาจหมายถึงว่าp < q
จะเกิดขึ้นกับพอยน์เตอร์ไปยังวัตถุต่าง ๆ เนื่องจากการใช้งานที่กำหนดเป้าหมายโมเดล "หน่วยความจำขนาดใหญ่" โดยปกติแล้วจะเลือกให้พอยน์เตอร์ ดูพอยน์เตอร์ที่อยู่ใกล้ไกลและไกลขนาดไหน - คอมไพเลอร์ C ตัวจริงสำหรับโหมดจริง x86 มีตัวเลือกในการคอมไพล์สำหรับโมเดล "ใหญ่" ที่พอยน์เตอร์เริ่มต้นทั้งหมดเป็น "ใหญ่" เว้นแต่จะมีการประกาศเป็นอย่างอื่น
x86 การแบ่งส่วนโหมดจริงไม่ใช่รูปแบบหน่วยความจำแบบไม่แบนเท่านั้นที่เป็นไปได้มันเป็นเพียงตัวอย่างที่เป็นประโยชน์เพื่อแสดงให้เห็นว่ามันได้รับการจัดการอย่างไรโดยการประยุกต์ใช้ C / C ++ ในชีวิตจริงการใช้งานขยาย ISO C ด้วยแนวคิดของfar
vs. near
pointers ทำให้โปรแกรมเมอร์สามารถเลือกได้เมื่อพวกเขาสามารถออกไปได้ด้วยการเก็บ / ส่งผ่านส่วนออฟเซ็ต 16 บิตเทียบกับส่วนข้อมูลทั่วไปบางส่วน
แต่การใช้งาน ISO C บริสุทธิ์นั้นจะต้องเลือกระหว่างโมเดลหน่วยความจำขนาดเล็ก (ทุกอย่างยกเว้นรหัสใน 64kiB เดียวกันกับตัวชี้ 16 บิต) หรือใหญ่หรือใหญ่โดยตัวชี้ทั้งหมดเป็น 32 บิต บางลูปสามารถปรับให้เหมาะสมโดยการเพิ่มเฉพาะส่วนออฟเซ็ต แต่วัตถุตัวชี้ไม่สามารถปรับให้เล็กลงได้
ถ้าคุณรู้ว่าสิ่งที่การจัดการเวทมนตร์สำหรับการดำเนินงานใดก็ตามที่คุณสามารถใช้มันใน C ปัญหาคือระบบที่แตกต่างกันใช้การกำหนดแอดเดรสที่แตกต่างกันและรายละเอียดไม่ได้ถูกกำหนดพารามิเตอร์โดยมาโครแบบพกพาใด ๆ
หรืออาจไม่ใช่: มันอาจเกี่ยวข้องกับการค้นหาบางสิ่งบางอย่างจากตารางเซ็กเมนต์พิเศษหรือบางสิ่งบางอย่างเช่นโหมดที่ได้รับการป้องกัน x86 แทนโหมดจริงที่ส่วนของที่อยู่เซ็กเมนต์นั้นเป็นดัชนีไม่ใช่ค่าที่จะเลื่อนไป คุณสามารถตั้งค่าส่วนที่ทับซ้อนกันบางส่วนในโหมดที่ได้รับการป้องกันและส่วนตัวเลือกส่วนของที่อยู่ไม่จำเป็นต้องเรียงลำดับตามที่อยู่เดียวกันกับที่อยู่ฐานของส่วนที่เกี่ยวข้อง การรับที่อยู่เชิงเส้นจากตัวชี้ seg: off ในโหมดที่ได้รับการป้องกัน x86 อาจเกี่ยวข้องกับการเรียกของระบบหาก GDT และ / หรือ LDT ไม่ได้รับการแมปลงในหน้าเว็บที่อ่านได้ในกระบวนการของคุณ
(แน่นอนว่าระบบปฏิบัติการหลักสำหรับ x86 ใช้โมเดลหน่วยความจำแบบแบนดังนั้นฐานเซ็กเมนต์จะเป็น 0 เสมอ (ยกเว้นหน่วยเก็บข้อมูลเธรดโลคัลที่ใช้fs
หรือgs
เซ็กเมนต์) และใช้เฉพาะส่วน "ออฟเซ็ต" แบบ 32 บิตหรือ 64 บิตเป็นตัวชี้ .)
คุณสามารถเพิ่มรหัสสำหรับแพลตฟอร์มเฉพาะต่าง ๆ ด้วยตนเองเช่นโดยค่าเริ่มต้นถือว่าแบนหรือ#ifdef
สิ่งที่จะตรวจจับโหมด x86 จริงและแบ่งออกuintptr_t
เป็นครึ่ง 16 บิตseg -= off>>4; off &= 0xf;
จากนั้นรวมชิ้นส่วนเหล่านั้นกลับเป็นจำนวน 32 บิต