เหตุใดจึงใช้ศูนย์ที่อยู่สำหรับตัวชี้ค่าว่าง


121

ใน C (หรือ C ++ สำหรับเรื่องนั้น) พอยน์เตอร์มีความพิเศษหากมีค่าเป็นศูนย์: ฉันขอแนะนำให้ตั้งค่าพอยน์เตอร์เป็นศูนย์หลังจากปลดปล่อยหน่วยความจำเพราะหมายความว่าการปล่อยพอยน์เตอร์อีกครั้งจะไม่เป็นอันตราย เมื่อฉันเรียก malloc มันจะส่งคืนตัวชี้ที่มีค่าเป็นศูนย์หากไม่สามารถรับหน่วยความจำได้ ฉันใช้if (p != 0)เวลาทั้งหมดเพื่อให้แน่ใจว่าพอยน์เตอร์ที่ผ่านนั้นถูกต้อง ฯลฯ

แต่เนื่องจากการกำหนดแอดเดรสหน่วยความจำเริ่มต้นที่ 0 จึงไม่ใช่ที่อยู่ที่ถูกต้องเหมือนที่อยู่อื่น ๆ ใช่หรือไม่? 0 จะใช้ในการจัดการตัวชี้ค่าว่างได้อย่างไรหากเป็นเช่นนั้น เหตุใดจำนวนลบจึงไม่เป็นค่าว่างแทน


แก้ไข:

คำตอบที่ดีมากมาย ฉันจะสรุปสิ่งที่พูดในคำตอบที่แสดงในขณะที่ใจของฉันตีความมันและหวังว่าชุมชนจะแก้ไขฉันหากฉันเข้าใจผิด

  • เช่นเดียวกับสิ่งอื่น ๆ ในการเขียนโปรแกรมมันเป็นนามธรรม เพียงแค่คงไม่เกี่ยวข้องจริงๆที่จะอยู่ 0. C ++ 0x nullptrเน้นนี้โดยการเพิ่มคำหลัก

  • มันไม่ได้เป็นนามธรรมที่อยู่ แต่เป็นค่าคงที่ที่กำหนดโดยมาตรฐาน C และคอมไพเลอร์สามารถแปลเป็นจำนวนอื่นได้ตราบเท่าที่ตรวจสอบให้แน่ใจว่าจะไม่เท่ากับที่อยู่ "จริง" และเท่ากับพอยน์เตอร์ว่างอื่น ๆ หาก 0 ไม่ใช่ คุ้มค่าที่สุดที่จะใช้สำหรับแพลตฟอร์ม

  • ในกรณีที่ไม่ใช่สิ่งที่เป็นนามธรรมซึ่งในช่วงแรก ๆ ระบบจะใช้แอดเดรส 0 และปิดขีด ​​จำกัด ของโปรแกรมเมอร์

  • ข้อเสนอแนะจำนวนลบของฉันเป็นการระดมความคิดเล็กน้อยฉันยอมรับ การใช้จำนวนเต็มที่ลงนามสำหรับแอดเดรสนั้นเป็นการสิ้นเปลืองเล็กน้อยหากหมายความว่านอกเหนือจากตัวชี้ null (-1 หรืออะไรก็ตาม) ช่องว่างของค่าจะถูกแบ่งเท่า ๆ กันระหว่างจำนวนเต็มบวกที่สร้างแอดเดรสที่ถูกต้องและจำนวนลบที่เสียไป

  • หากตัวเลขใด ๆ สามารถแทนได้ด้วยประเภทข้อมูลจะเป็น 0 (อาจเป็น 1 เกินไปฉันคิดว่าจำนวนเต็มหนึ่งบิตซึ่งจะเป็น 0 หรือ 1 หากไม่ได้ลงชื่อหรือเพียงแค่บิตที่เซ็นชื่อหากลงชื่อหรือจำนวนเต็มสองบิตซึ่ง จะเป็น [-2, 1] แต่คุณสามารถไปหา 0 เป็นค่าว่างและ 1 เป็นไบต์เดียวที่เข้าถึงได้ในหน่วยความจำ)

ยังคงมีบางสิ่งที่ค้างคาอยู่ในใจของฉัน ตัวชี้คำถาม Stack Overflow ไปยังที่อยู่คงที่ที่ระบุบอกฉันว่าแม้ว่า 0 สำหรับตัวชี้ค่าว่างจะเป็นนามธรรม แต่ค่าตัวชี้อื่น ๆ ก็ไม่จำเป็น สิ่งนี้ทำให้ฉันต้องโพสต์คำถาม Stack Overflow อีกครั้งฉันต้องการเข้าถึงที่อยู่ศูนย์หรือไม่ .


11
คุณได้อย่างง่ายดายเพียงอาจมีการเปลี่ยนแปลงif (p != 0)ไปif (p)ซึ่งเป็นสำนวนที่พบบ่อยใน C และ C ++ ถึงแม้ว่าคุณจะต้องได้รับออกมาจากนิสัยถ้าคุณใช้เวลาถึง Java
Paul Tomblin

14
การลบบางอย่างสองครั้งหมายความว่ารหัสของคุณผิด ฉันไม่แนะนำให้ตั้งค่าพอยน์เตอร์เป็นโมฆะเพื่อให้คุณสามารถหยุดทำงานและแก้ไขปัญหาได้ ไม่ว่าในกรณีใดคุณทำผิดพลาดโดยถือว่าที่อยู่เป็นจำนวนเต็ม นี้ไม่จำเป็นต้องเป็นจริงและ 0 เพียงหมายถึงบางค่าตัวชี้ที่เกิดขึ้นจริงซึ่งคือการดำเนินการเฉพาะ "ที่อยู่เชิงลบ" ไม่สมเหตุสมผลตามแนวคิด
GManNickG

6
@GMan: อาจเป็นความคิดที่ดีที่จะตั้งค่าตัวชี้ไปยังที่อยู่ซึ่งจะบังคับให้เกิดข้อขัดข้องเช่น0xDEADBEEF.
Billy ONeal

5
คำถามที่ไม่มีวันตาย!

8
@ Noah: จุดถูกตั้งค่าเป็น null -> ซ่อนข้อผิดพลาดในการเขียนโปรแกรมอย่าตั้งค่าเป็น null -> ค้นหาข้อผิดพลาดในการเขียนโปรแกรม ฉันไม่รู้เกี่ยวกับคุณ แต่ฉันต้องการให้รหัสของฉันถูกต้อง
GManNickG

คำตอบ:


65

2 คะแนน:

  • เฉพาะค่าคงที่ 0 ในซอร์สโค้ดเท่านั้นที่เป็นตัวชี้ null การใช้งานคอมไพเลอร์สามารถใช้ค่าใดก็ได้ที่ต้องการหรือต้องการในโค้ดที่รัน บางแพลตฟอร์มมีค่าตัวชี้พิเศษที่ 'ไม่ถูกต้อง' ซึ่งการใช้งานอาจใช้เป็นตัวชี้ค่าว่าง คำถามที่พบบ่อยเกี่ยวกับ C มีคำถาม"อย่างจริงจังมีเครื่องจริง ๆ ใช้พอยน์เตอร์ที่ไม่ใช่ศูนย์จริง ๆ หรือไม่ ซึ่งชี้ให้เห็นหลายแพลตฟอร์มที่ใช้คุณสมบัตินี้ของ 0 เป็นตัวชี้ค่าว่างในแหล่งที่มา C ในขณะที่แสดงต่างกันที่รันไทม์ มาตรฐาน C ++ มีหมายเหตุที่ทำให้ชัดเจนว่าการแปลง "นิพจน์ค่าคงที่แบบอินทิกรัลที่มีค่าเป็นศูนย์จะให้ตัวชี้ค่าว่างเสมอ

  • ค่าลบอาจใช้งานได้เช่นเดียวกับแพลตฟอร์มที่เป็นแอดเดรส - มาตรฐาน C เพียงแค่ต้องเลือกสิ่งที่จะใช้เพื่อระบุตัวชี้ว่างและเลือกศูนย์ ฉันไม่แน่ใจจริงๆว่าค่า Sentinel อื่น ๆ ได้รับการพิจารณาหรือไม่

ข้อกำหนดเดียวสำหรับตัวชี้ค่าว่างคือ:

  • รับประกันว่าจะเปรียบเทียบไม่เท่ากันกับตัวชี้กับวัตถุจริง
  • พอยน์เตอร์ว่างสองตัวใด ๆ จะเปรียบเทียบค่าเท่ากัน (C ++ ปรับแต่งสิ่งนี้เพื่อให้สิ่งนี้จำเป็นสำหรับพอยน์เตอร์เป็นประเภทเดียวกันเท่านั้น)

12
+1 ฉันสงสัยว่า 0 ถูกเลือกด้วยเหตุผลทางประวัติศาสตร์เท่านั้น (0 เป็นที่อยู่เริ่มต้นและไม่ถูกต้องเกือบตลอดเวลา) แน่นอนว่าโดยทั่วไปแล้วสมมติฐานดังกล่าวไม่ได้เป็นจริงเสมอไป แต่ 0 ทำงานได้ดี
GManNickG

8
พื้นที่อาจเป็นปัจจัยสนับสนุน ในสมัยที่ C ถูกพัฒนาขึ้นครั้งแรกหน่วยความจำมีราคาแพงกว่าในปัจจุบัน เลขศูนย์สามารถคำนวณได้อย่างสะดวกโดยใช้คำสั่ง XOR หรือไม่จำเป็นต้องโหลดค่าทันที สิ่งนี้อาจช่วยประหยัดพื้นที่ได้ทั้งนี้ขึ้นอยู่กับสถาปัตยกรรม
Sparky

6
@GMan - คุณถูกต้อง ในซีพียูรุ่นแรกศูนย์แอดเดรสหน่วยความจำเป็นสิ่งพิเศษและมีการป้องกันฮาร์ดแวร์จากการเข้าถึงจากซอฟต์แวร์ที่รันอยู่ (ในบางกรณีเป็นการเริ่มต้นของเวกเตอร์การรีเซ็ตและการแก้ไขอาจทำให้ CPU ไม่สามารถรีเซ็ตหรือเริ่มทำงานได้) โปรแกรมเมอร์ใช้การป้องกันฮาร์ดแวร์นี้เป็นรูปแบบของการตรวจจับข้อผิดพลาดในซอฟต์แวร์ของตนโดยปล่อยให้ที่อยู่ของ CPU ถอดรหัสตรรกะตรวจหาตัวชี้ที่ไม่ได้เริ่มต้นหรือไม่ถูกต้องแทนที่จะต้องใช้คำแนะนำของ CPU ในการดำเนินการ การประชุมยังคงอยู่จนถึงทุกวันนี้แม้ว่าจุดประสงค์ของที่อยู่ศูนย์อาจเปลี่ยนไป
bta

10
คอมไพเลอร์ Minix 16 บิตใช้ 0xFFFF สำหรับ NULL
Joshua

3
ในระบบฝังตัวจำนวนมาก 0 คือที่อยู่ที่ถูกต้อง ค่า -1 (บิตทั้งหมด) เป็นแอดเดรสที่ถูกต้องเช่นกัน Checksums สำหรับ ROM นั้นยากที่จะคำนวณเมื่อข้อมูลเริ่มต้นที่ที่อยู่ 0 :-(
Thomas Matthews

31

ในอดีตพื้นที่แอดเดรสเริ่มต้นที่ 0 จะเป็น ROM เสมอซึ่งใช้สำหรับระบบปฏิบัติการบางระบบหรือขั้นตอนการจัดการขัดจังหวะระดับต่ำในปัจจุบันเนื่องจากทุกอย่างเป็นเสมือน (รวมถึงพื้นที่ที่อยู่) ระบบปฏิบัติการจึงสามารถจับคู่การจัดสรรกับที่อยู่ใดก็ได้ดังนั้นจึงสามารถ โดยเฉพาะไม่จัดสรรอะไรที่อยู่ 0


6
มันสวยมาก เป็นไปตามแบบแผนทางประวัติศาสตร์และที่อยู่ตัวแรกถูกใช้สำหรับตัวจัดการขัดจังหวะดังนั้นจึงไม่สามารถใช้งานได้สำหรับโปรแกรมปกติ นอกจากนี้ 0 คือ "ว่าง" ซึ่งสามารถตีความได้ว่าไม่มีค่า / ไม่มีตัวชี้
TomTom

15

IIRC ค่า "null pointer" ไม่รับประกันว่าจะเป็นศูนย์ คอมไพลเลอร์แปล 0 เป็นค่า "null" ใด ๆ ที่เหมาะสมสำหรับระบบ (ซึ่งในทางปฏิบัติอาจเป็นศูนย์เสมอไป แต่ไม่จำเป็น) คำแปลเดียวกันจะถูกนำไปใช้ทุกครั้งที่คุณเปรียบเทียบตัวชี้กับศูนย์ เนื่องจากคุณสามารถเปรียบเทียบพอยน์เตอร์ซึ่งกันและกันและเทียบกับค่าพิเศษ -0 นี้ได้จึงป้องกันโปรแกรมเมอร์ไม่ให้รู้อะไรเกี่ยวกับการแสดงหน่วยความจำของระบบ สำหรับเหตุผลที่พวกเขาเลือก 0 แทนที่จะเป็น 42 หรือเช่นนั้นฉันจะเดาว่าเป็นเพราะโปรแกรมเมอร์ส่วนใหญ่เริ่มนับที่ 0 :) (นอกจากนี้ในระบบส่วนใหญ่ 0 เป็นที่อยู่หน่วยความจำแรกและพวกเขาต้องการให้สะดวกตั้งแต่ใน ฝึกฝนการแปลเหมือนที่ฉันกำลังอธิบายอยู่แทบจะไม่เกิดขึ้นจริงภาษาก็อนุญาตสำหรับพวกเขา)


5
@ จัสติน: คุณเข้าใจผิด ค่าคงที่ 0 คือเสมอตัวชี้โมฆะ สิ่งที่ @meador พูดคือเป็นไปได้ว่าตัวชี้โมฆะ (ระบุด้วยค่าคงที่ 0) ไม่ตรงกับศูนย์ที่อยู่ ในบางแพลตฟอร์มการสร้างตัวชี้ค่าว่าง ( int* p = 0) อาจสร้างตัวชี้ที่มีค่า0xdeadbeefหรือค่าอื่น ๆ ที่ต้องการ 0 เป็นตัวชี้ค่าว่าง แต่ตัวชี้โมฆะไม่จำเป็นต้องเป็นตัวชี้ที่อยู่ศูนย์ :)
jalf

ตัวชี้ NULL เป็นค่าที่สงวนไว้และขึ้นอยู่กับคอมไพเลอร์อาจเป็นรูปแบบบิตใดก็ได้ ตัวชี้ NULL ไม่ได้หมายความว่าชี้ไปที่ที่อยู่ 0
Sharjeel Aziz

3
แต่ @Jalf ค่าคงที่ 0 ไม่ใช่ตัวชี้โมฆะเสมอไป เป็นสิ่งที่เราเขียนเมื่อเราต้องการให้คอมไพเลอร์กรอกตัวชี้ค่าว่างที่แท้จริงของแพลตฟอร์มให้เรา ในทางปฏิบัติตัวชี้โมฆะมักจะตรงกับศูนย์ที่อยู่และฉันตีความคำถามของ Joel ว่าทำไมถึงเป็นเช่นนั้น ที่อยู่นั้นควรจะมีหน่วยความจำไบต์ที่ถูกต้องดังนั้นทำไมไม่ใช้ที่อยู่ที่ไม่มีอยู่จริงของไบต์ที่ไม่มีอยู่จริงแทนที่จะลบไบต์ที่ถูกต้องออกจากการเล่น (ฉันกำลังเขียนสิ่งที่ฉันคิดว่าโจเอลกำลังคิดไม่ใช่คำถามที่ฉันถามตัวเอง)
Rob Kennedy

@Rob: เรียงลำดับจาก. ฉันรู้ว่าคุณหมายถึงอะไรและคุณถูกต้อง แต่ฉันก็เช่นกัน :) จำนวนเต็มคงที่ 0 แทนตัวชี้ค่าว่างในระดับซอร์สโค้ด การเปรียบเทียบตัวชี้ค่าว่างกับ 0 จะให้ค่าเป็นจริง การกำหนด 0 ให้กับตัวชี้จะทำให้ตัวชี้นั้นเป็นโมฆะ 0 คือตัวชี้โมฆะ แต่การแทนค่าจริงในหน่วยความจำของตัวชี้ค่าว่างอาจแตกต่างจากรูปแบบศูนย์บิต (อย่างไรก็ตามความคิดเห็นของฉันตอบสนองต่อความคิดเห็นที่ลบไปแล้วในตอนนี้ของ @ จัสตินไม่ใช่คำถามของ @ Joel :)
jalf

@jalf @Rob คุณต้องการคำศัพท์เพื่อชี้แจงฉันคิดว่า :) จาก§4.10 / 1: " ค่าคงที่ของตัวชี้ค่าว่างคือค่านิพจน์คงที่ปริพันธ์ของชนิดจำนวนเต็มที่ประเมินเป็นศูนย์ค่าคงที่ของตัวชี้ค่าว่างสามารถแปลงเป็นชนิดตัวชี้ได้ผลลัพธ์คือค่าตัวชี้ค่าว่างของชนิดนั้นและ สามารถแยกแยะได้จากค่าอื่น ๆ ของตัวชี้ไปยังวัตถุหรือตัวชี้ไปยังประเภทฟังก์ชัน "
GManNickG

15

คุณต้องเข้าใจความหมายของค่าคงที่ศูนย์ในบริบทตัวชี้ผิด

ทั้งใน C หรือในตัวชี้ C ++ ไม่สามารถ "มีค่าเป็นศูนย์" ได้ ตัวชี้ไม่ใช่วัตถุทางคณิตศาสตร์ พวกมันมีค่าตัวเลขเช่น "ศูนย์" หรือ "ลบ" หรืออะไรก็ได้ในลักษณะนั้น ดังนั้นคำกล่าวของคุณเกี่ยวกับ "พอยน์เตอร์ ... มีค่าเป็นศูนย์" ก็ไม่สมเหตุสมผล

ใน C & C ++ ชี้สามารถมีสงวนค่า null ชี้ การแทนค่าที่แท้จริงของค่าพอยน์เตอร์แบบ null ไม่มีส่วนเกี่ยวข้องกับ "ศูนย์" ใด ๆ อาจเป็นอะไรก็ได้ที่เหมาะสมอย่างยิ่งสำหรับแพลตฟอร์มที่กำหนด เป็นความจริงที่ว่าค่า null-pointer บน plaforms ส่วนใหญ่จะแสดงทางกายภาพด้วยค่าแอดเดรสที่เป็นศูนย์จริง อย่างไรก็ตามหากในบางแพลตฟอร์มแอดเดรส 0 ถูกใช้เพื่อวัตถุประสงค์บางอย่าง (เช่นคุณอาจต้องสร้างอ็อบเจ็กต์ที่แอดเดรส 0) ค่าตัวชี้ค่า null บนแพลตฟอร์มดังกล่าวมักจะแตกต่างกัน อาจแสดงทางกายภาพเป็น0xFFFFFFFFค่าที่อยู่หรือเป็น0xBAADBAADค่าที่อยู่ตัวอย่างเช่น

แต่ไม่คำนึงถึงวิธีการที่คุ้มค่า null ชี้เป็น respresented บนแพลตฟอร์มที่กำหนดในรหัสของคุณคุณจะยังคงดำเนินการต่อเพื่อกำหนดตัวชี้ null 0โดยคงที่ เพื่อที่จะกำหนดค่า null p = 0ชี้ไปชี้ที่กำหนดคุณจะยังคงใช้การแสดงออกเช่น เป็นความรับผิดชอบของคอมไพเลอร์ในการตระหนักถึงสิ่งที่คุณต้องการและแปลเป็นการแสดงค่า null-pointer ที่เหมาะสมเช่นแปลเป็นรหัสที่จะใส่ค่า address ของ0xFFFFFFFFตัวชี้pเป็นต้น

ในระยะสั้นความจริงที่ว่าคุณใช้0ในรหัส SORCE ของคุณเพื่อสร้างค่า null ชี้ไม่ได้หมายความว่าค่า Null 0ตัวชี้จะเชื่อมโยงอย่างใดที่จะอยู่ สิ่ง0ที่คุณใช้ในซอร์สโค้ดของคุณเป็นเพียง "น้ำตาลเชิงไวยากรณ์" ที่ไม่มีความสัมพันธ์กับที่อยู่จริงจริงที่ค่าตัวชี้ค่า null คือ "ชี้"


3
<quote> พอยน์เตอร์ไม่ใช่วัตถุเลขคณิต </quote> ตัวชี้เลขคณิตถูกกำหนดไว้ค่อนข้างดีใน C และ C ++ ส่วนหนึ่งของข้อกำหนดคือพอยน์เตอร์ทั้งสองชี้ภายในคอมโพสิตเดียวกัน ตัวชี้ค่าว่างไม่ได้ชี้ไปที่องค์ประกอบใด ๆ ดังนั้นการใช้ตัวชี้นี้ในนิพจน์ทางคณิตศาสตร์ของตัวชี้จึงไม่ถูกต้อง (p1 - nullptr) - (p2 - nullptr) == (p1 - p2)ยกตัวอย่างเช่นมันจะไม่รับประกันว่า
Ben Voigt

5
@ Ben Voigt: สเปคภาษากำหนดความคิดของประเภทเลขคณิต ทั้งหมดที่ฉันพูดก็คือประเภทตัวชี้ไม่ได้อยู่ในหมวดหมู่ของประเภทเลขคณิต เลขคณิตของตัวชี้เป็นเรื่องราวที่แตกต่างและไม่เกี่ยวข้องกันโดยสิ้นเชิงเป็นเพียงเรื่องบังเอิญทางภาษา
AnT

1
คนที่อ่านวัตถุทางคณิตศาสตร์ควรจะรู้ได้อย่างไรว่ามันหมายถึง "ในความหมายของประเภทเลขคณิต" ไม่ใช่ "ในความหมายของตัวดำเนินการทางคณิตศาสตร์" (ซึ่งหลายตัวใช้งานได้กับพอยน์เตอร์) หรือ "ในความหมายของตัวชี้เลขคณิต" เท่าที่บังเอิญภาษาที่มีความกังวลวัตถุทางคณิตศาสตร์มีตัวอักษรมากเหมือนกันกับเลขคณิตชี้กว่าประเภทเลขคณิต ในเวลาเดียวกันมาตรฐานไม่พูดคุยเกี่ยวกับค่าตัวชี้ โปสเตอร์ต้นฉบับอาจหมายถึงการแทนค่าจำนวนเต็มของตัวชี้มากกว่าค่าตัวชี้และNULLไม่จำเป็นต้องแสดงด้วย 0 อย่างชัดเจน
Ben Voigt

ตัวอย่างเช่นคำว่าวัตถุสเกลาร์ในคำศัพท์ C / C ++ เป็นเพียงการชวเลขสำหรับวัตถุประเภทสเกลาร์ (เช่นเดียวกับวัตถุ POD = วัตถุประเภท POD ) ผมใช้ระยะวัตถุทางคณิตศาสตร์ในทางเดียวกันว่าหมายถึงวัตถุประเภทเลขคณิต ฉันคาดหวังให้ "ใครบางคน" เข้าใจแบบนั้น คนที่ไม่สามารถขอคำชี้แจงได้เสมอ
AnT

1
ฉันทำงานกับระบบที่ (เท่าที่เกี่ยวข้องกับฮาร์ดแวร์) null คือ 0xffffffff และ 0 เป็นที่อยู่ที่ถูกต้องสมบูรณ์
pm100

8

แต่เนื่องจากการกำหนดแอดเดรสหน่วยความจำเริ่มต้นที่ 0 จึงไม่ใช่ที่อยู่ที่ถูกต้องเหมือนที่อยู่อื่น ๆ ใช่หรือไม่?

ในระบบปฏิบัติการบางระบบ / หลาย / ทั้งหมดที่อยู่หน่วยความจำ 0 เป็นสิ่งพิเศษในบางวิธี ตัวอย่างเช่นมักจะแมปกับหน่วยความจำที่ไม่ถูกต้อง / ไม่มีอยู่จริงซึ่งทำให้เกิดข้อยกเว้นหากคุณพยายามเข้าถึง

เหตุใดจำนวนลบจึงไม่เป็นค่าว่างแทน

ฉันคิดว่าโดยทั่วไปแล้วค่าตัวชี้จะถือว่าเป็นตัวเลขที่ไม่ได้ลงชื่อมิฉะนั้นเช่นตัวชี้ 32 บิตจะสามารถระบุหน่วยความจำได้เพียง 2 GB แทนที่จะเป็น 4 GB


4
ฉันได้เขียนโค้ดบนอุปกรณ์โดยที่ address zero เป็นที่อยู่ที่ถูกต้องและไม่มีการป้องกันหน่วยความจำ พอยน์เตอร์ Null ยังเป็นบิต - ศูนย์ทั้งหมด หากคุณเขียนไปยังตัวชี้ค่าว่างโดยไม่ได้ตั้งใจคุณก็ทำลายการตั้งค่าระบบปฏิบัติการที่อยู่ที่ที่อยู่ศูนย์ ความเฮฮามักจะไม่เกิดขึ้น
MM

1
ใช่: บนไม่ใช่โหมดป้องกัน x86 CPU เช่นที่อยู่ 0 เป็นตารางขัดจังหวะเวกเตอร์
ChrisW

@ChrisW: บน x86 โหมดที่ไม่ได้รับการป้องกันโดยเฉพาะที่อยู่ศูนย์คือเวกเตอร์ขัดจังหวะหารด้วยศูนย์ซึ่งบางโปรแกรมอาจมีเหตุผลที่ถูกต้องทั้งหมดในการเขียน
SuperCat

แม้บนแพลตฟอร์มที่พื้นที่เก็บข้อมูลที่ใช้งานได้จะเริ่มต้นที่ที่อยู่ทางกายภาพศูนย์การใช้งาน C สามารถใช้ที่อยู่เป็นศูนย์เพื่อเก็บวัตถุที่ไม่มีที่อยู่ได้อย่างง่ายดายหรือเพียงแค่ปล่อยให้คำแรกของหน่วยความจำไม่ได้ใช้ ในแพลตฟอร์มส่วนใหญ่การเปรียบเทียบกับศูนย์จะบันทึกคำสั่งเมื่อเทียบกับการเปรียบเทียบกับสิ่งอื่น ๆ ดังนั้นการเสียพื้นที่เก็บข้อมูลคำแรกจะถูกกว่าการใช้ที่อยู่ที่ไม่ใช่ศูนย์สำหรับค่า null โปรดทราบว่าไม่มีข้อกำหนดว่าที่อยู่ของสิ่งที่ไม่ครอบคลุมโดยมาตรฐาน C (เช่นพอร์ต I / O หรือเวกเตอร์ขัดจังหวะ) เปรียบเทียบไม่เท่ากันกับค่าว่างหรือว่า ...
supercat

... ระบบประมวลผล null-pointer เข้าถึงสิ่งที่แตกต่างจากที่อื่นดังนั้น all-bits-zero จึงเป็นแอดเดรสที่ดีสำหรับ "null" แม้ในระบบที่การเข้าถึงศูนย์ตำแหน่งทางกายภาพจะมีประโยชน์และมีความหมาย
supercat

5

ฉันเดาว่าค่าวิเศษ 0 ถูกเลือกเพื่อกำหนดตัวชี้ที่ไม่ถูกต้องเนื่องจากสามารถทดสอบได้โดยมีคำสั่งน้อยกว่า ภาษาเครื่องบางภาษาจะตั้งค่าแฟล็กศูนย์และเซ็นชื่อโดยอัตโนมัติตามข้อมูลเมื่อโหลดรีจิสเตอร์เพื่อให้คุณสามารถทดสอบตัวชี้ค่าว่างด้วยการโหลดอย่างง่ายจากนั้นและแยกคำสั่งโดยไม่ต้องทำคำสั่งเปรียบเทียบแยกต่างหาก

(ISA ส่วนใหญ่ตั้งค่าแฟล็กบนคำสั่ง ALU เท่านั้นไม่ใช่โหลดและโดยปกติคุณจะไม่สร้างพอยน์เตอร์ผ่านการคำนวณยกเว้นในคอมไพเลอร์เมื่อแยกวิเคราะห์แหล่งที่มา C แต่อย่างน้อยคุณก็ไม่จำเป็นต้องมีค่าคงที่ความกว้างของตัวชี้โดยพลการเพื่อ เปรียบเทียบกับ.)

ใน Commodore Pet, Vic20 และ C64 ซึ่งเป็นเครื่องแรกที่ฉันใช้ RAM เริ่มต้นที่ตำแหน่ง 0 ดังนั้นจึงสามารถอ่านและเขียนได้โดยใช้ตัวชี้ค่าว่างหากคุณต้องการจริงๆ


3

ฉันคิดว่ามันเป็นแค่อนุสัญญา ต้องมีค่าบางอย่างเพื่อทำเครื่องหมายตัวชี้ที่ไม่ถูกต้อง

คุณเสียพื้นที่แอดเดรสไปหนึ่งไบต์ซึ่งแทบจะไม่มีปัญหา

ไม่มีตัวชี้เชิงลบ พอยน์เตอร์มักจะไม่ได้ลงนาม นอกจากนี้หากอาจเป็นลบการประชุมของคุณก็หมายความว่าคุณเสียพื้นที่ที่อยู่ไปครึ่งหนึ่ง


หมายเหตุ: คุณจะไม่สูญเสียพื้นที่ที่อยู่ไป คุณจะได้รับตัวชี้ไปยังที่อยู่ 0 char *p = (char *)1; --p;ด้วยการทำ: เนื่องจากพฤติกรรมบนตัวชี้ค่าว่างไม่ได้กำหนดโดยมาตรฐานระบบนี้จึงสามารถpอ่านและเขียนที่อยู่ 0 เพิ่มขึ้นเพื่อให้ที่อยู่1ฯลฯ
MM

@MattMcNabb: การใช้งานโดยที่ address zero เป็นฮาร์ดแวร์แอดเดรสที่ถูกต้องอาจกำหนดลักษณะการทำงานของchar x = ((char*)0);การอ่านแอดเดรสเป็นศูนย์และจัดเก็บค่านั้นเป็น x ได้อย่างถูกต้องตามกฎหมาย โค้ดดังกล่าวจะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดในการนำไปใช้งานใด ๆ ที่ไม่ได้กำหนดพฤติกรรมของมัน แต่ความจริงที่ว่ามาตรฐานกล่าวว่าบางสิ่งบางอย่างเป็นพฤติกรรมที่ไม่ได้กำหนดในลักษณะที่ห้ามไม่ให้การนำไปใช้งานโดยเสนอข้อกำหนดของตนเองสำหรับสิ่งที่จะทำ
supercat

@supercat ITYM *(char *)0. นั่นเป็นความจริง แต่ในคำแนะนำของฉันการใช้งานไม่จำเป็นต้องกำหนดลักษณะการทำงานของ*(char *)0หรือการดำเนินการตัวชี้ว่างอื่น ๆ
MM

1
@MattMcNabb: พฤติกรรมของchar *p = (char*)1; --p;จะถูกกำหนดโดยมาตรฐานก็ต่อเมื่อลำดับนั้นถูกดำเนินการหลังจากที่ตัวชี้ไปยังสิ่งอื่นที่ไม่ใช่ไบต์แรกของวัตถุถูกเหวี่ยงไปที่ a intptr_tและผลลัพธ์ของการร่ายนั้นเกิดขึ้นเพื่อให้ได้ค่า 1 และในกรณีที่โดยเฉพาะอย่างยิ่งผลมาจากการ--pจะให้ผลผลิตตัวชี้ไปยังไบต์หนึ่งก่อนที่มีค่าตัวชี้เมื่อนักแสดงที่จะได้ผลintptr_t 1
supercat

3

แม้ว่า C จะใช้ 0 เพื่อแสดงถึงตัวชี้โมฆะ แต่อย่าลืมว่าค่าของตัวชี้นั้นอาจไม่ใช่ศูนย์ อย่างไรก็ตามโปรแกรมเมอร์ส่วนใหญ่จะใช้ระบบที่ตัวชี้ค่าว่างคือ 0 เท่านั้น

แต่ทำไมถึงเป็นศูนย์? มันเป็นที่อยู่เดียวที่ทุกระบบใช้ร่วมกัน และบ่อยครั้งที่อยู่ที่ต่ำจะถูกสงวนไว้สำหรับวัตถุประสงค์ของระบบปฏิบัติการดังนั้นค่านี้จึงทำงานได้ดีจนไม่สามารถ จำกัด โปรแกรมแอปพลิเคชันได้ การกำหนดค่าจำนวนเต็มให้กับตัวชี้โดยไม่ตั้งใจมีแนวโน้มที่จะลงเอยด้วยศูนย์อย่างอื่น


3
เหตุผลที่เป็นไปได้มากกว่าที่อยู่เบื้องหลังทั้งหมดนี้คือ: มีราคาถูกที่จะแจกหน่วยความจำที่เตรียมใช้งานล่วงหน้าให้เป็นศูนย์และสะดวกที่จะให้ค่าในหน่วยความจำนั้นแสดงถึงสิ่งที่มีความหมายเช่นจำนวนเต็ม 0, ทศนิยม 0.0 และพอยน์เตอร์ว่าง ข้อมูลคงที่ใน C ที่เริ่มต้นเป็นศูนย์ / ศูนย์ไม่จำเป็นต้องใช้พื้นที่ใด ๆ ในไฟล์ปฏิบัติการและถูกแมปกับบล็อกที่เติมเป็นศูนย์เมื่อโหลด Zero อาจได้รับการปฏิบัติเป็นพิเศษในภาษาเครื่องเช่นการเปรียบเทียบศูนย์ง่ายๆเช่น "branch if เท่ากับศูนย์" เป็นต้น MIPS ยังมีการลงทะเบียนจำลองที่เป็นเพียงค่าคงที่เป็นศูนย์
Kaz

2

ในอดีตหน่วยความจำต่ำของแอปพลิเคชันถูกครอบครองโดยทรัพยากรระบบ ในสมัยนั้นศูนย์กลายเป็นค่าว่างเริ่มต้น

แม้ว่าสิ่งนี้ไม่จำเป็นต้องเป็นจริงสำหรับระบบสมัยใหม่ แต่ก็ยังเป็นความคิดที่ดีที่จะตั้งค่าตัวชี้เป็นอะไรก็ได้ยกเว้นการจัดสรรหน่วยความจำที่มอบให้คุณ


2

เกี่ยวกับข้อโต้แย้งเรื่องการไม่ตั้งค่าตัวชี้เป็น null หลังจากลบไปเพื่อให้ในอนาคตลบ "expose error" ...

หากคุณกังวลจริงๆเกี่ยวกับเรื่องนี้แนวทางที่ดีกว่าซึ่งรับประกันได้ว่าจะได้ผลคือการใช้ประโยชน์จากการยืนยัน ():


...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...

สิ่งนี้ต้องการการพิมพ์พิเศษและการตรวจสอบเพิ่มเติมหนึ่งครั้งในระหว่างการสร้างดีบัก แต่แน่นอนว่าจะให้สิ่งที่คุณต้องการ: สังเกตว่าเมื่อ ptr ถูกลบ 'สองครั้ง' ทางเลือกที่ระบุในการอภิปรายความคิดเห็นไม่ใช่การตั้งค่าตัวชี้เป็นโมฆะดังนั้นคุณจะได้รับความผิดพลาดไม่รับประกันว่าจะประสบความสำเร็จ ที่แย่กว่านั้นคืออาจทำให้เกิดความผิดพลาด (หรือแย่กว่านั้นมาก!) กับผู้ใช้หาก "ข้อบกพร่อง" เหล่านี้ผ่านไปยังชั้นวาง สุดท้ายเวอร์ชันนี้ช่วยให้คุณสามารถรันโปรแกรมต่อไปเพื่อดูว่าเกิดอะไรขึ้นจริง

ฉันรู้ว่านี่ไม่ได้ตอบคำถามที่ถาม แต่ฉันกังวลว่าคนที่อ่านความคิดเห็นอาจได้ข้อสรุปว่ามันถือเป็น 'แนวปฏิบัติที่ดี' ที่จะไม่ตั้งค่าตัวชี้เป็น 0 หากเป็นไปได้ที่พวกเขาจะถูกส่งไปยัง free () หรือ ลบสองครั้ง ในบางกรณีที่เป็นไปได้ไม่ควรใช้แนวปฏิบัติที่ดีในการใช้พฤติกรรมที่ไม่ได้กำหนดเป็นเครื่องมือในการดีบัก ไม่มีใครที่ต้องค้นหาข้อผิดพลาดที่เกิดจากการลบตัวชี้ที่ไม่ถูกต้องจะเสนอสิ่งนี้ ข้อผิดพลาดประเภทนี้ใช้เวลาหลายชั่วโมงในการค้นหาและเกือบจะส่งผลต่อโปรแกรมด้วยวิธีที่ไม่คาดคิดโดยสิ้นเชิงซึ่งยากที่จะไม่สามารถติดตามกลับไปที่ปัญหาเดิมได้


2

เหตุผลสำคัญที่ระบบปฏิบัติการจำนวนมากใช้ all-bits-zero สำหรับการแทนค่า null พอยน์เตอร์นั่นคือวิธีการนี้memset(struct_with_pointers, 0, sizeof struct_with_pointers)และวิธีการที่คล้ายกันจะตั้งค่าพอยน์เตอร์ทั้งหมดภายในstruct_with_pointersเป็นพอยน์เตอร์ว่าง สิ่งนี้ไม่ได้รับการรับรองโดยมาตรฐาน C แต่หลาย ๆ โปรแกรมก็คิดเช่นนั้น


1

ในเครื่อง DEC รุ่นเก่าเครื่องหนึ่ง (ฉันคิดว่า PDP-8) รันไทม์ C จะปกป้องหน่วยความจำหน้าแรกของหน่วยความจำดังนั้นการพยายามเข้าถึงหน่วยความจำในบล็อกนั้นจะทำให้เกิดข้อยกเว้นขึ้น


PDP-8 ไม่มีคอมไพเลอร์ C PDP-11 ไม่มีการป้องกันหน่วยความจำและ VAX นั้นน่าอับอายสำหรับการคืนค่า 0 ไปยังการกำหนดค่าตัวชี้ NULL อย่างเงียบ ๆ ฉันไม่แน่ใจว่านี่หมายถึงเครื่องใด
fuz

1

การเลือกค่า sentinel นั้นเป็นไปตามอำเภอใจและในความเป็นจริงแล้ว C ++ รุ่นถัดไปได้รับการแก้ไข (เรียกอย่างไม่เป็นทางการว่า "C ++ 0x" ซึ่งส่วนใหญ่จะรู้จักกันในชื่อ ISO C ++ 2011 ในอนาคต) ด้วยการเปิดตัว คีย์เวิร์ดnullptrเพื่อแทนตัวชี้ค่าว่าง ใน C ++ อาจใช้ค่า 0 เป็นนิพจน์เริ่มต้นสำหรับ POD ใด ๆ และสำหรับอ็อบเจ็กต์ใด ๆ ที่มีคอนสตรัคเตอร์เริ่มต้นและมีความหมายพิเศษในการกำหนดค่า sentinel ในกรณีของการกำหนดค่าเริ่มต้นของตัวชี้ เหตุใดจึงไม่เลือกค่าลบโดยปกติที่อยู่จะอยู่ในช่วง 0 ถึง 2 N-1 สำหรับค่าบางค่า N กล่าวอีกนัยหนึ่งที่อยู่มักจะถือว่าเป็นค่าที่ไม่ได้ลงนาม หากใช้ค่าสูงสุดเป็นค่า Sentinel ค่านั้นจะต้องแตกต่างกันไปในแต่ละระบบขึ้นอยู่กับขนาดของหน่วยความจำในขณะที่ 0 เป็นที่อยู่ที่แสดงได้เสมอ นอกจากนี้ยังใช้เพื่อเหตุผลทางประวัติศาสตร์เนื่องจากโดยทั่วไปแล้วที่อยู่หน่วยความจำ 0 ไม่สามารถใช้งานได้ในโปรแกรมและในปัจจุบันระบบปฏิบัติการส่วนใหญ่มีบางส่วนของเคอร์เนลที่โหลดไว้ในหน้าที่ด้านล่างของหน่วยความจำและโดยทั่วไปหน้าดังกล่าวจะได้รับการป้องกันในลักษณะที่หาก สัมผัส (dereferenced) โดยโปรแกรม (บันทึกเคอร์เนล) จะทำให้เกิดข้อผิดพลาด


1

มันจะต้องมีค่าบางอย่าง เห็นได้ชัดว่าคุณไม่ต้องการก้าวไปที่ค่าที่ผู้ใช้อาจต้องการใช้อย่างถูกกฎหมาย ฉันจะคาดเดาว่าเนื่องจากรันไทม์ C จัดเตรียมเซ็กเมนต์ BSS สำหรับข้อมูลที่เริ่มต้นเป็นศูนย์จึงมีความสมเหตุสมผลในระดับหนึ่งที่จะแปลค่าศูนย์เป็นค่าตัวชี้ที่ไม่ได้เริ่มต้น


0

ระบบปฏิบัติการไม่ค่อยอนุญาตให้คุณเขียนถึงที่อยู่ 0 เป็นเรื่องปกติที่จะติดสิ่งที่เฉพาะเจาะจงของระบบปฏิบัติการลงในหน่วยความจำต่ำ กล่าวคือ IDTs ตารางหน้า ฯลฯ (ตารางต้องอยู่ใน RAM และง่ายกว่าที่จะติดไว้ที่ด้านล่างมากกว่าการพยายามกำหนดว่าด้านบนของ RAM อยู่ที่ใด) และไม่มีระบบปฏิบัติการใดในใจที่ถูกต้องจะช่วยให้คุณ แก้ไขตารางระบบโดยเจตนา

สิ่งนี้อาจไม่ได้อยู่ในความคิดของ K & R เมื่อพวกเขาสร้าง C แต่มัน (พร้อมกับความจริงที่ว่า 0 == null นั้นค่อนข้างจำง่าย) ทำให้ 0 เป็นตัวเลือกยอดนิยม


สิ่งนี้ไม่เป็นความจริงในโหมดป้องกันและในความเป็นจริงในการกำหนดค่า Linux บางอย่างคุณสามารถเขียนไปยังที่อยู่เสมือน 0 ได้
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

0

ค่า0นี้เป็นค่าพิเศษที่ใช้กับความหมายต่างๆในนิพจน์เฉพาะ ในกรณีของพอยน์เตอร์ดังที่ได้มีการชี้ให้เห็นหลาย ๆ ครั้งอาจมีการใช้คำนี้เนื่องจากในขณะนั้นเป็นวิธีที่สะดวกที่สุดในการพูดว่า "ใส่ค่า Sentinel เริ่มต้นที่นี่" ในฐานะนิพจน์คงไม่มีความหมายเท่ากับศูนย์บิต (กล่าวคือบิตทั้งหมดตั้งค่าเป็นศูนย์) ในบริบทของนิพจน์พอยน์เตอร์ ใน C ++ มีหลายประเภทที่ไม่มีการแทนค่าเป็นศูนย์ในระดับบิตNULLเช่นสมาชิกตัวชี้และฟังก์ชันตัวชี้ไปยังสมาชิก

Thankfully, C ++ 0x มีคำหลักใหม่สำหรับ "การแสดงออกที่หมายถึงตัวชี้ไม่ถูกต้องที่รู้จักกันว่ายังไม่ได้แมปไป Bitwise nullptrศูนย์สำหรับการแสดงออกทางหนึ่ง" แม้ว่าจะมีระบบไม่กี่ระบบที่คุณสามารถกำหนดเป้าหมายด้วย C ++ ที่อนุญาตให้มีการอ้างอิงที่อยู่ 0 โดยไม่ต้องใช้ barfing ดังนั้นโปรแกรมเมอร์โปรดระวัง


0

มีคำตอบที่ดีมากมายในหัวข้อนี้ อาจมีสาเหตุหลายประการในการเลือกใช้ค่า0สำหรับพอยน์เตอร์ว่าง แต่ฉันจะเพิ่มอีกสองอย่าง:

  • ใน C ++ การเริ่มต้นตัวชี้เป็นศูนย์จะตั้งค่าให้เป็นโมฆะ
  • ในโปรเซสเซอร์หลายตัวจะมีประสิทธิภาพมากกว่าในการตั้งค่าเป็น 0 หรือเพื่อทดสอบว่ามันเท่ากับ / ไม่เท่ากับ 0 เมื่อเทียบกับค่าคงที่อื่น ๆ

0

สิ่งนี้ขึ้นอยู่กับการนำพอยน์เตอร์ไปใช้ใน C / C ++ ไม่มีเหตุผลที่เฉพาะเจาะจงว่าทำไม NULL จึงเทียบเท่าในการกำหนดให้กับตัวชี้


-1

มีเหตุผลทางประวัติศาสตร์สำหรับเรื่องนี้ แต่ก็มีเหตุผลในการเพิ่มประสิทธิภาพเช่นกัน

เป็นเรื่องปกติที่ระบบปฏิบัติการจะจัดเตรียมกระบวนการที่มีเพจหน่วยความจำเริ่มต้นเป็น 0 หากโปรแกรมต้องการแปลความหมายส่วนหนึ่งของเพจหน่วยความจำนั้นเป็นตัวชี้แสดงว่าเป็น 0 ดังนั้นจึงง่ายพอสำหรับโปรแกรมที่จะระบุว่าตัวชี้นั้นเป็น ไม่ได้เริ่มต้น (สิ่งนี้ใช้ได้ไม่ดีเมื่อใช้กับหน้าแฟลชที่ไม่ได้เริ่มต้น)

อีกเหตุผลหนึ่งก็คือในโปรเซสเซอร์หลายตัวมันง่ายมากที่จะทดสอบการเทียบเท่าของค่าเป็น 0 บางครั้งก็เป็นการเปรียบเทียบฟรีโดยไม่จำเป็นต้องมีคำแนะนำเพิ่มเติมใด ๆ และโดยปกติแล้วสามารถทำได้โดยไม่จำเป็นต้องให้ค่าเป็นศูนย์ในรีจิสเตอร์อื่นหรือ เป็นตัวอักษรในสตรีมคำสั่งเพื่อเปรียบเทียบกับ

การเปรียบเทียบราคาถูกสำหรับโปรเซสเซอร์ส่วนใหญ่คือเซ็นชื่อน้อยกว่า 0 และเท่ากับ 0 (เซ็นชื่อมากกว่า 0 และไม่เท่ากับ 0 เป็นนัยโดยทั้งสองอย่างนี้)

เนื่องจาก 1 ค่าจากค่าที่เป็นไปได้ทั้งหมดจะต้องถูกสงวนไว้เป็นค่าที่ไม่ดีหรือไม่ได้เริ่มต้นดังนั้นคุณจึงอาจทำให้เป็นค่าที่มีการทดสอบที่ถูกที่สุดสำหรับการเทียบเท่ากับค่าที่ไม่ถูกต้อง นอกจากนี้ยังเป็นจริงสำหรับสตริงอักขระที่ถูกยกเลิก "\ 0"

หากคุณพยายามใช้มากกว่าหรือน้อยกว่า 0 เพื่อจุดประสงค์นี้คุณจะต้องตัดช่วงที่อยู่ของคุณเป็นครึ่งหนึ่ง


-2

คง0ถูกนำมาใช้แทนNULLเพราะ C ที่ถูกสร้างขึ้นโดยบางส่วนล้านล้าน cavemen ของปีที่ผ่านมาNULL, NIL, ZIPหรือจะมีทั้งหมดทำให้ความรู้สึกมากกว่าNADDA0

แต่เนื่องจากการกำหนดแอดเดรสหน่วยความจำเริ่มต้นที่ 0 จึงไม่ใช่ที่อยู่ที่ถูกต้องเหมือนที่อยู่อื่น ๆ ใช่หรือไม่?

จริง แม้ว่าระบบปฏิบัติการจำนวนมากจะไม่อนุญาตให้คุณทำแผนที่อะไรก็ตามที่ศูนย์ที่อยู่แม้ว่าจะอยู่ในพื้นที่ที่อยู่เสมือน (ผู้คนตระหนักว่าภาษา C เป็นภาษาที่ไม่ปลอดภัยและสะท้อนให้เห็นว่าจุดบกพร่องของตัวชี้ค่าว่างเป็นเรื่องธรรมดามาก แต่ก็ตัดสินใจที่จะ "แก้ไข" โดยไม่อนุญาต รหัสพื้นที่ผู้ใช้เพื่อแมปกับหน้า 0 ดังนั้นหากคุณโทรกลับ แต่ตัวชี้การเรียกกลับเป็น NULL คุณจะไม่ต้องใช้รหัสใด ๆ โดยพลการ)

0 จะใช้ในการจัดการตัวชี้ค่าว่างได้อย่างไรหากเป็นเช่นนั้น

เนื่องจาก0ใช้ในการเปรียบเทียบกับตัวชี้จะถูกแทนที่ด้วยค่าเฉพาะการนำไปใช้งานบางอย่างซึ่งเป็นค่าที่ส่งคืนของ malloc จากความล้มเหลวของ malloc

เหตุใดจำนวนลบจึงไม่เป็นค่าว่างแทน

นี่จะยิ่งสับสน


ประเด็นของคุณเกี่ยวกับ "มนุษย์ถ้ำ" ฯลฯ อาจอยู่ที่ต้นตอของมันแม้ว่าฉันคิดว่าข้อมูลจำเพาะแตกต่างกัน รูปแบบแรกสุดของสิ่งที่พัฒนามาเป็น C ได้รับการออกแบบมาเพื่อทำงานบนสถาปัตยกรรมเฉพาะโดยที่intไม่เพียง แต่มีขนาดเท่ากับตัวชี้เท่านั้นในหลาย ๆ บริบทintและตัวชี้สามารถใช้แทนกันได้ หากรูทีนต้องการตัวชี้และตัวชี้ผ่านเป็นจำนวนเต็ม 57 รูทีนจะใช้แอดเดรสที่มีรูปแบบบิตเดียวกับหมายเลข 57 บนเครื่องเหล่านั้นรูปแบบบิตที่ใช้แสดงตัวชี้โมฆะคือ 0 ดังนั้นการส่ง int 0 จะส่งผ่านตัวชี้โมฆะ
supercat

ตั้งแต่นั้นเป็นต้นมา C ได้พัฒนาขึ้นเพื่อให้สามารถใช้เขียนโปรแกรมสำหรับเครื่องอื่น ๆ ได้หลากหลายโดยมีการแสดงตัวเลขและตัวชี้ต่างกัน ในขณะที่ค่าคงที่ตัวเลขที่ไม่ใช่ศูนย์แทบจะไม่ถูกใช้เป็นตัวชี้ แต่ศูนย์ตัวเลขคงที่ถูกนำมาใช้กันอย่างแพร่หลายเพื่อแสดงถึงพอยน์เตอร์ว่าง การไม่อนุญาตการใช้งานดังกล่าวจะทำให้โค้ดที่มีอยู่เสียหายดังนั้นจึงคาดว่าคอมไพเลอร์จะแปลค่าศูนย์ตัวเลขเป็นสิ่งที่การใช้งานใช้แทนตัวชี้ค่าว่าง
supercat

-4

(โปรดอ่านย่อหน้านี้ก่อนอ่านโพสต์ ฉันขอให้ทุกคนที่สนใจอ่านโพสต์นี้ควรพยายามอ่านอย่างละเอียดและแน่นอนว่าอย่าลงคะแนนจนกว่าคุณจะเข้าใจอย่างสมบูรณ์ขอบคุณ)

ตอนนี้เป็นวิกิชุมชนแล้วหากมีผู้ไม่เห็นด้วยกับแนวคิดใด ๆ โปรดแก้ไขพร้อมคำอธิบายที่ชัดเจนและละเอียดว่าอะไรผิดและทำไมและถ้าเป็นไปได้โปรดอ้างอิงแหล่งที่มาหรือแสดงหลักฐานที่สามารถทำซ้ำได้

ตอบ

นี่คือสาเหตุอื่น ๆ บางส่วนที่อาจเป็นปัจจัยพื้นฐานสำหรับ NULL == 0

  1. ความจริงที่ว่าศูนย์เป็นเท็จดังนั้นเราสามารถทำได้โดยตรงif(!my_ptr)แทนif(my_ptr==NULL)แทน
  2. ความจริงที่ว่าจำนวนเต็มโกลบอลที่ไม่ได้เริ่มต้นจะถูกกำหนดค่าเริ่มต้นโดยค่าเริ่มต้นเป็นศูนย์ทั้งหมดและด้วยเหตุนี้ตัวชี้ของศูนย์ทั้งหมดจะถือว่าไม่ได้เริ่มต้น

ที่นี่ฉันอยากจะพูดคำเกี่ยวกับคำตอบอื่น ๆ

ไม่ใช่เพราะน้ำตาลสังเคราะห์

การบอกว่า NULL เป็นศูนย์เนื่องจากน้ำตาลในรูปแบบไวยากรณ์ไม่สมเหตุสมผลมากเกินไปถ้าอย่างนั้นทำไมไม่ใช้ดัชนี 0 ของอาร์เรย์เพื่อเก็บความยาว

ในความเป็นจริงภาษา C เป็นภาษาที่ใกล้เคียงกับการใช้งานภายในมากที่สุดมันสมเหตุสมผลหรือไม่ที่จะบอกว่า C เลือกศูนย์เพราะน้ำตาลวากยสัมพันธ์ พวกเขาต้องการให้คีย์เวิร์ดเป็นโมฆะ (เช่นเดียวกับภาษาอื่น ๆ ) แทนที่จะแมปศูนย์เป็นโมฆะ!

ในขณะที่ ณ วันนี้มันอาจเป็นเพียงแค่น้ำตาลในการสังเคราะห์ แต่ก็เป็นที่ชัดเจนว่าความตั้งใจเดิมของผู้พัฒนาภาษาซีไม่ได้มีไว้สำหรับน้ำตาลวากยสัมพันธ์ดังที่ฉันจะแสดงต่อไป

1) ข้อกำหนด

แม้ว่าจะเป็นความจริงที่ข้อกำหนด C พูดจากค่าคงที่ 0 เป็นตัวชี้ค่าว่าง (ส่วน 6.3.2.3) และยังกำหนดค่า NULL ที่จะนำไปใช้งาน (ส่วน 7.19 ในข้อกำหนด C11 และ 7.17 ในข้อกำหนด C99) ความจริงก็คือในหนังสือ "ภาษาโปรแกรมซี" ที่เขียนโดยผู้ประดิษฐ์ภาษาซีมีการระบุไว้ในส่วน 5.4:

C รับประกันว่าศูนย์ไม่ใช่ที่อยู่ที่ถูกต้องสำหรับข้อมูลดังนั้นจึงสามารถใช้ค่าที่ส่งคืนเป็นศูนย์เพื่อส่งสัญญาณเหตุการณ์ผิดปกติได้ในกรณีนี้จะไม่มีช่องว่าง

ตัวชี้และจำนวนเต็มไม่สามารถใช้แทนกันได้ศูนย์เป็นข้อยกเว้นเพียงอย่างเดียว: ค่าคงที่ศูนย์อาจถูกกำหนดให้กับตัวชี้และตัวชี้อาจเปรียบเทียบกับศูนย์คงที่ ค่าคงที่ที่เป็นสัญลักษณ์มักใช้แทนศูนย์เพื่อช่วยในการจำเพื่อระบุให้ชัดเจนยิ่งขึ้นว่านี่เป็นค่าพิเศษสำหรับตัวชี้ NULL ถูกกำหนดใน. เราจะใช้ NULL ต่อจากนี้ไป

อย่างที่เราเห็น (จากคำว่า "ที่อยู่ศูนย์") อย่างน้อยความตั้งใจเดิมของผู้เขียน C คือศูนย์ที่อยู่ไม่ใช่ศูนย์คงที่นอกจากนี้ยังปรากฏจากข้อความที่ตัดตอนมานี้ว่าสาเหตุที่ข้อกำหนดนั้นพูดจาก ค่าคงที่ศูนย์อาจไม่รวมนิพจน์ที่ประเมินค่าเป็นศูนย์ แต่ให้รวมค่าคงที่จำนวนเต็มศูนย์เป็นค่าคงที่จำนวนเต็มเดียวที่อนุญาตให้ใช้ในบริบทตัวชี้โดยไม่ต้องแคสต์

2) สรุป

ในขณะที่ข้อกำหนดไม่ได้กล่าวอย่างชัดเจนว่าที่อยู่ศูนย์สามารถปฏิบัติได้แตกต่างจากค่าคงที่เป็นศูนย์ แต่ก็ไม่ได้บอกว่าไม่และความจริงเมื่อจัดการกับค่าคงที่ของตัวชี้ nullก็ไม่ได้อ้างว่าเป็นการใช้งานที่กำหนดไว้ ทำโดยค่าคงที่กำหนดค่า NULLแทนที่จะอ้างว่าเป็นศูนย์แสดงว่าอาจมีความแตกต่างระหว่างค่าคงที่เป็นศูนย์และที่อยู่ศูนย์

(อย่างไรก็ตามหากเป็นกรณีนี้ฉันแค่สงสัยว่าเหตุใดจึงมีการกำหนดการใช้งาน NULL เนื่องจากในกรณีเช่นนี้ NULL อาจเป็นศูนย์คงที่ได้เช่นกันเนื่องจากคอมไพเลอร์ต้องแปลงค่าคงที่เป็นศูนย์ทั้งหมดให้เป็นค่า NULL ที่กำหนดไว้ในการนำไปใช้งานจริง)

อย่างไรก็ตามฉันไม่เห็นสิ่งนี้ในการดำเนินการจริงและในแพลตฟอร์มทั่วไปที่อยู่ศูนย์และศูนย์คงที่จะได้รับการปฏิบัติเหมือนกันและส่งข้อความแสดงข้อผิดพลาดเดียวกัน

นอกจากนี้ความจริงก็คือระบบปฏิบัติการในปัจจุบันกำลังสำรองหน้าแรกทั้งหมด (ช่วง 0x0000 ถึง 0xFFFF) เพียงเพื่อป้องกันการเข้าถึงที่อยู่ศูนย์เนื่องจากตัวชี้ NULL ของ C (ดูhttp://en.wikipedia.org/wiki/ Zero_pageเช่นเดียวกับ "Windows Via C / C ++ โดย Jeffrey Richter และ Christophe Nasarre (เผยแพร่โดย Microsoft Press)")

ดังนั้นฉันจะขอจากใครก็ตามที่อ้างว่าได้เห็นการทำงานจริงโปรดระบุแพลตฟอร์มและคอมไพเลอร์และรหัสที่แน่นอนที่เขาทำจริง (แม้ว่าจะมีคำจำกัดความที่คลุมเครือในข้อกำหนด [ดังที่ฉันได้แสดง] คอมไพเลอร์ใด ๆ และแพลตฟอร์มมีอิสระที่จะทำทุกอย่างที่เขาต้องการ)

อย่างไรก็ตามดูเหมือนว่าผู้เขียนของ C ไม่ได้คำนึงถึงเรื่องนี้และพวกเขากำลังพูดถึง "ที่อยู่ศูนย์" และ "C รับประกันว่าที่อยู่นี้ไม่มีวันถูกต้อง" เช่นเดียวกับ "NULL เป็นเพียง ช่วยในการจำ "ซึ่งแสดงให้เห็นอย่างชัดเจนว่าเจตนาเดิมไม่ได้มีไว้สำหรับ" น้ำตาลวากยสัมพันธ์ "

ไม่ใช่เพราะระบบปฏิบัติการ

นอกจากนี้ยังอ้างว่าระบบปฏิบัติการปฏิเสธการเข้าถึงที่อยู่ศูนย์ด้วยเหตุผลบางประการ:

1) เมื่อเขียน C ไม่มีข้อ จำกัด ดังที่เห็นในวิกิพีเดียนี้http://en.wikipedia.org/wiki/Zero_page http://en.wikipedia.org/wiki/Zero_page

2) ความจริงก็คือคอมไพเลอร์ C เข้าถึงหน่วยความจำที่อยู่เป็นศูนย์

สิ่งนี้ดูเหมือนจะเป็นข้อเท็จจริงจากบทความต่อไปนี้ของ BellLabs ( http://www.cs.bell-labs.com/who/dmr/primevalC.html )

คอมไพเลอร์ทั้งสองแตกต่างกันในรายละเอียดในการรับมือกับสิ่งนี้ ในก่อนหน้านี้การเริ่มต้นจะพบได้โดยการตั้งชื่อฟังก์ชัน ในภายหลังการเริ่มต้นจะกลายเป็น 0 เท่านั้นซึ่งบ่งชี้ว่าคอมไพเลอร์ตัวแรกถูกเขียนขึ้นก่อนที่เราจะมีเครื่องที่มีการแมปหน่วยความจำดังนั้นจุดเริ่มต้นของโปรแกรมไม่ได้อยู่ที่ตำแหน่ง 0 ในขณะที่เวลาของวินาที เรามี PDP-11 ที่จัดทำแผนที่

(ในความเป็นจริง ณ วันนี้ (ตามที่ฉันอ้างถึงข้างต้นจาก wikipedia และ microsoft press) สาเหตุของการ จำกัด การเข้าถึงที่อยู่เป็นศูนย์นั้นเป็นเพราะตัวชี้ NULL ของ C ดังนั้นในตอนท้ายมันก็กลายเป็นอีกทางหนึ่ง!)

3) โปรดจำไว้ว่า C ยังใช้ในการเขียนระบบปฏิบัติการและแม้แต่คอมไพเลอร์ C!

ในความเป็นจริง C ได้รับการพัฒนาเพื่อจุดประสงค์ในการเขียนระบบปฏิบัติการ UNIX ด้วยเหตุนี้จึงดูเหมือนจะไม่มีเหตุผลว่าทำไมพวกเขาจึงควร จำกัด ตัวเองจากศูนย์ที่อยู่

(ฮาร์ดแวร์) คำอธิบายเกี่ยวกับวิธีที่คอมพิวเตอร์ (ทางกายภาพ) สามารถเข้าถึง Address Zero ได้

มีอีกประเด็นหนึ่งที่ฉันต้องการจะอธิบายที่นี่เป็นไปได้อย่างไรที่จะอ้างอิงที่อยู่ศูนย์เลย?

คิดว่าเป็นวินาทีที่อยู่จะถูกดึงโดยโปรเซสเซอร์จากนั้นส่งเป็นแรงดันไฟฟ้าบนบัสหน่วยความจำซึ่งระบบหน่วยความจำจะใช้เพื่อไปยังที่อยู่จริง แต่ที่อยู่เป็นศูนย์จะหมายความว่าไม่มีแรงดันไฟฟ้า ดังนั้นฮาร์ดแวร์ทางกายภาพของระบบหน่วยความจำจึงเข้าถึงที่อยู่เป็นศูนย์ได้อย่างไร?

คำตอบดูเหมือนจะเป็นศูนย์ที่อยู่นั้นเป็นค่าเริ่มต้นและกล่าวอีกนัยหนึ่งที่อยู่ศูนย์จะสามารถเข้าถึงได้โดยระบบหน่วยความจำเสมอเมื่อบัสหน่วยความจำปิดอยู่อย่างสมบูรณ์และด้วยเหตุนี้คำขอใด ๆ ที่จะอ่านหรือเขียนโดยไม่ระบุที่อยู่จริง (ซึ่ง คือกรณีที่มีที่อยู่เป็นศูนย์) จะเข้าถึงที่อยู่ศูนย์โดยอัตโนมัติ


1
ฉันไม่ได้ลงคะแนนให้คุณ แต่โพสต์ของคุณมีความไม่ถูกต้องตามข้อเท็จจริงหลายประการเช่น หน่วยความจำฟิสิคัลที่ออฟเซ็ต 0 นั้นไม่สามารถเข้าถึงได้ (เนื่องจากสวิตช์ทั้งหมดถูกปิดจริงเหรอ?) 0 และค่าคงที่ 0 แทนกันได้ (อาจไม่ใช่) และอื่น ๆ
Hasturkun

เกี่ยวกับ 0 และศูนย์คงที่นี่คือสิ่งที่หนังสือต้นฉบับกล่าวและนี่คือสิ่งที่แสดงการทดสอบจริงคุณพบความแตกต่างที่แท้จริงระหว่างทั้งสองหรือไม่? ถ้าใช่คอมไพเลอร์และแพลตฟอร์มใด ในขณะที่คำตอบจำนวนมากชี้ให้เห็นว่ามีความแตกต่างที่ฉันไม่พบและไม่มีการอ้างอิงเพื่อแสดงความแตกต่าง ในความเป็นจริงอ้างอิงจากen.wikipedia.org/wiki/Zero_pageและยังมี "Windows Via C / C ++ โดย Jeffrey Richter และ Christophe Nasarre (เผยแพร่โดย Microsoft Press)" ทั้งหน้าแรก! ได้รับการปกป้องในคอมพิวเตอร์สมัยใหม่เพื่อป้องกันไม่ให้ null (เสียมากกว่าหนึ่งไบต์จริงๆ!)
yoel halb

แน่นอนว่าจะใช้รูปแบบบิตของแอดเดรสเพื่อเลือกสิ่งที่กำลังอ่าน โดยทั่วไปเป็นกรณี อย่างไรก็ตามฉันไม่ต้องการโต้แย้งกับคุณฉันแค่ชี้ให้เห็นว่าทำไมคุณถึงถูกลดคะแนน
Hasturkun

ฉันไม่เห็นด้วยกับการเรียกร้องของคุณ ฉันยังไม่สนใจที่จะดำเนินการสนทนานี้ต่อไป
Hasturkun

6
การอ้างสิทธิ์ฮาร์ดแวร์เป็นเรื่องไร้สาระ หากต้องการอ่านที่อยู่เป็นศูนย์ไดรฟ์! ชิปเลือกต่ำ! RAS สูง! CAS ต่ำ! เราสูงและบรรทัดที่อยู่ทั้งหมดต่ำ เมื่อรถดับ! CS สูง
MSalters
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.