มาตรฐาน C ไม่ต้องการให้พอยน์เตอร์ว่างอยู่ที่ศูนย์แอดเดรสของเครื่อง อย่างไรก็ตามการส่ง0
ค่าคงที่เป็นค่าตัวชี้จะต้องส่งผลให้NULL
ตัวชี้ (§6.3.2.3 / 3) และการประเมินค่าตัวชี้ว่างเป็นบูลีนจะต้องเป็นเท็จ นี้สามารถเป็นบิตที่น่าอึดอัดใจจริงๆถ้าคุณไม่ต้องการที่อยู่ที่ศูนย์และNULL
ไม่ได้เป็นที่อยู่ศูนย์
อย่างไรก็ตามด้วยการปรับเปลี่ยนคอมไพลเลอร์และไลบรารีมาตรฐาน (หนัก) จึงเป็นไปไม่ได้ที่NULL
จะแสดงด้วยรูปแบบบิตทางเลือกในขณะที่ยังคงเป็นไปตามไลบรารีมาตรฐานอย่างเคร่งครัด มันเป็นไม่เพียงพอที่จะเพียงแค่เปลี่ยนคำจำกัดความของNULL
ตัวเอง แต่เป็นแล้วNULL
จะประเมินให้เป็นจริง
โดยเฉพาะคุณจะต้อง:
- จัดให้มีศูนย์ที่แท้จริงในการมอบหมายงานที่จะชี้ (หรือปลดเปลื้องเพื่อชี้) ที่จะแปลงเป็นค่าความมหัศจรรย์บางอย่างอื่น ๆ
-1
เช่น
- จัดให้มีการทดสอบความเท่าเทียมกันระหว่างพอยน์เตอร์และจำนวนเต็มคงที่
0
เพื่อตรวจสอบค่าเวทมนตร์แทน (§6.5.9 / 6)
- จัดเรียงสำหรับบริบททั้งหมดที่ชนิดตัวชี้ถูกประเมินเป็นบูลีนเพื่อตรวจสอบความเท่าเทียมกับค่าวิเศษแทนที่จะตรวจสอบเป็นศูนย์ สิ่งนี้ตามมาจากความหมายของการทดสอบความเท่าเทียมกัน แต่คอมไพเลอร์อาจนำไปใช้ภายในที่แตกต่างกัน ดู§6.5.13 / 3, §6.5.14 / 3, §6.5.15 / 4, §6.5.3.3 / 5, §6.8.4.1 / 2, §6.8.5 / 4
- ตามที่ Caf ชี้ให้อัปเดตความหมายสำหรับการเริ่มต้นของวัตถุคงที่ (§6.7.8 / 10) และตัวเริ่มต้นผสมบางส่วน (§6.7.8 / 21) เพื่อสะท้อนการแสดงตัวชี้ค่าว่างใหม่
- สร้างวิธีอื่นในการเข้าถึงศูนย์ที่อยู่จริง
มีบางสิ่งที่คุณไม่ต้องจัดการ ตัวอย่างเช่น:
int x = 0;
void *p = (void*)x;
หลังจากนี้p
ไม่รับประกันว่าจะเป็นตัวชี้โมฆะ ต้องจัดการเฉพาะการกำหนดค่าคงที่เท่านั้น (นี่เป็นแนวทางที่ดีสำหรับการเข้าถึงที่อยู่จริงเป็นศูนย์) ในทำนองเดียวกัน:
int x = 0;
assert(x == (void*)0); // CAN BE FALSE
นอกจากนี้:
void *p = NULL;
int x = (int)p;
x
ไม่รับประกันว่าจะเป็น 0
ไม่รับประกันว่าจะเป็น
ในระยะสั้นเห็นได้ชัดว่าเงื่อนไขนี้ได้รับการพิจารณาโดยคณะกรรมการภาษาซีและมีการพิจารณาสำหรับผู้ที่จะเลือกตัวแทนอื่นสำหรับ NULL สิ่งที่คุณต้องทำตอนนี้คือทำการเปลี่ยนแปลงที่สำคัญกับคอมไพเลอร์ของคุณและเดี๋ยวก่อนคุณทำเสร็จแล้ว :)
ตามหมายเหตุด้านข้างอาจเป็นไปได้ที่จะใช้การเปลี่ยนแปลงเหล่านี้ด้วยขั้นตอนการแปลงซอร์สโค้ดก่อนที่คอมไพเลอร์จะเหมาะสม นั่นคือแทนที่จะเป็นโฟลว์ปกติของตัวประมวลผลล่วงหน้า -> คอมไพเลอร์ -> แอสเซมเบลอร์ -> ตัวเชื่อมโยงคุณจะต้องเพิ่มตัวประมวลผลล่วงหน้า -> การแปลงค่า NULL -> คอมไพเลอร์ -> แอสเซมเบลอร์ -> ตัวเชื่อม จากนั้นคุณสามารถทำการเปลี่ยนแปลงเช่น:
p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }
สิ่งนี้จะต้องใช้ตัวแยกวิเคราะห์ C แบบเต็มรวมถึงตัวแยกวิเคราะห์ประเภทและการวิเคราะห์ typedefs และการประกาศตัวแปรเพื่อพิจารณาว่าตัวระบุใดที่สอดคล้องกับพอยน์เตอร์ อย่างไรก็ตามการทำเช่นนี้ทำให้คุณไม่ต้องทำการเปลี่ยนแปลงส่วนการสร้างโค้ดของคอมไพเลอร์ให้เหมาะสม เสียงดังกราวอาจมีประโยชน์สำหรับการนำสิ่งนี้ไปใช้ - ฉันเข้าใจว่ามันถูกออกแบบมาโดยคำนึงถึงการเปลี่ยนแปลงเช่นนี้ คุณยังคงต้องทำการเปลี่ยนแปลงในไลบรารีมาตรฐานด้วยเช่นกัน
mprotect
รักษาความปลอดภัยได้ หรือถ้าแพลตฟอร์มไม่มี ASLR หรือสิ่งที่คล้ายกันจะอยู่นอกเหนือหน่วยความจำกายภาพของแพลตฟอร์ม โชคดี.