NULL เทียบกับศูนย์ใน C

ฉันได้อ่านเมื่อเร็วๆ นี้ ฉันสามารถใช้ NULL แทนค่า 0 ได้หรือไม่

กล่าวโดยย่อในคำตอบมีการกล่าวถึงว่าไม่แนะนำให้ใช้ NULL เป็นการทดแทนค่า 0 และจะนำไปสู่ ​​UB

แต่ใน ปลอดภัยหรือไม่ที่จะถือว่าค่าคงที่ NULL เป็นศูนย์ กล่าวโดยย่อว่าการถือว่า if(!ptr)//ptr is a pointer นั้นไม่ผิดทั้งหมด .

ฉันรู้ว่าเนื้อหาคำถามแตกต่างกัน แต่จะอธิบายได้อย่างไรว่าการใช้ NULL แทน 0 นั้นผิด ในขณะที่ if(!ptr) เป็นจริง เพราะ if(!ptr) เทียบเท่ากับ if(ptr==0) (ฉันถือว่านี่ถูกต้อง ไม่แน่ใจ)

นอกจากนี้ ฉันยังใช้ if(ptr==0) และมันก็ไม่เคยทำงานผิดสำหรับฉัน (เพื่อตรวจสอบว่า ptr เป็น NULL หรือไม่) และฉันได้ กำหนด 0 ให้กับตัวชี้ ptr และเมื่อฉันดีบั๊กโค้ดของฉัน ptr เป็น NULL< /แข็งแกร่ง>. ประสบการณ์ทั้งสองนี้ปลอดภัยหรือไม่?


person hanie    schedule 03.04.2020    source แหล่งที่มา


คำตอบ (3)


จากการอ้างอิง NULL ตัวชี้นี้:

ในการเริ่มต้นตัวชี้ให้เป็นโมฆะหรือกำหนดค่าว่างให้กับตัวชี้ที่มีอยู่ อาจใช้ค่าคงที่ของตัวชี้ค่าว่าง (NULL หรือค่าคงที่จำนวนเต็มอื่นๆ ที่มีค่าเป็นศูนย์)

[เน้นของฉัน]

ดังนั้นค่าคงที่จำนวนเต็ม 0 จึงเป็นค่าคงที่ตัวชี้ค่าว่างที่ถูกต้อง

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

นอกจากนี้ ตัวชี้ค่าว่างจะเป็น "เท็จ" เสมอ และตัวชี้ที่ไม่ใช่ค่าว่างจะเป็น "จริง" เสมอ ซึ่งเป็นสาเหตุที่เงื่อนไขเช่น if (ptr) หรือ if (!ptr) ทำงานได้ดี

person Some programmer dude    schedule 03.04.2020

ฉันรู้ว่าเนื้อหาคำถามแตกต่างกัน แต่จะอธิบายได้อย่างไรว่าการใช้ NULL แทน 0 นั้นผิด ในขณะที่ if(!ptr) เป็นจริง เพราะ if(!ptr) เทียบเท่ากับ if(ptr==0) (ฉันถือว่านี่ถูกต้อง ไม่แน่ใจ)

if(!ptr) เทียบเท่ากับ if (!ptr != 0) ตามซีแมนทิกส์ของคำสั่ง if ซึ่งเทียบเท่ากับ if (ptr == 0) ด้วยซีแมนทิกส์ของตัวดำเนินการ !, != และ == พร้อมตัวถูกดำเนินการของพอยน์เตอร์ สิ่งนี้มีความสอดคล้องกันเป็นอย่างดี แต่ไม่ได้เป็นไปตามสิ่งที่คุณรู้เกี่ยวกับการดำเนินการกับจำนวนเต็ม การดำเนินการกับพอยน์เตอร์มีกฎของตัวเอง

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

person John Bollinger    schedule 03.04.2020

NULL คือมาโคร มันเป็น "ค่าคงที่ตัวชี้โมฆะที่กำหนดการนำไปใช้งาน" C17dr § 7.19 3.

นิพจน์ค่าคงที่จำนวนเต็มที่มีค่า 0 หรือนิพจน์ที่แปลงเป็น void * เรียกว่า ค่าคงที่ตัวชี้ค่าว่าง C17dr § 6.3.2.3 3

ดังนั้น NULL อาจมีประเภทเป็น void *, int, long, unsigned, long long เป็นต้น

0 คือค่าคงที่ int

เมื่อทุกอย่างเรียบร้อย

การมอบหมาย: ทั้งสองด้านล่างกำหนด p,q ให้กับ ตัวชี้ว่าง

void *p = 0;
void *q = NULL; 

การเปรียบเทียบโค้ด: p==q เป็นจริง เนื่องจาก ตัวชี้ค่าว่าง ทั้งหมดเท่ากัน ตัวชี้ค่าว่างทั้งหมดไม่เท่ากับที่อยู่ของวัตถุใดๆ !p และ !q เป็น 1 ทั้งคู่

เมื่อมันไม่โอเค

อาร์กิวเมนต์ของฟังก์ชัน

ประเภทและขนาดของ NULL ถูกกำหนดการใช้งานแล้ว

printf("%d\n", 0);            // OK - %d expects an int
printf("%d\n", NULL);         // Not OK, NULL could be long, void *, etc.
printf("%p\n", NULL);         // Not OK, NULL could be int, long, long long
printf("%p\n", (void*) NULL); // OK - %p expects a void*

_Generic()

ผลลัพธ์ด้านล่างคือการดำเนินการที่กำหนดไว้

_Generic((NULL), \
  void *: "void *", \
  int: "int", \
  long: "long", \
  default: "TBD")

เปรียบเทียบมาโคร

ผลลัพธ์ด้านล่างทำให้เกิด "ข้อผิดพลาด: ตัวดำเนินการ '*' ไม่มีตัวถูกดำเนินการที่ถูกต้อง" สำหรับฉัน #if !0 สบายดี

#if !NULL
#error foo
#endif
person chux - Reinstate Monica    schedule 03.04.2020
comment
นี่ ptr=0 โอเคไหม? ฉันหมายถึง การเริ่มต้นนี้แทนที่จะเป็น ptr=NULL right ที่จะใช้หรือไม่ - person hanie; 03.04.2020
comment
การนำไปปฏิบัติอาจหรืออาจไม่รับประกันสิ่งใดเกี่ยวกับตัวอย่าง printf ทั้งหมด แต่ตัวอย่างที่ระบุว่ากำหนด NULL ด้วยวิธีบางอย่าง จะต้องประมวลผลบรรทัดที่สองหรือสามที่ไม่ตกลงในลักษณะที่มีความหมาย ขึ้นอยู่กับว่าพวกเขาระบุ NULL อย่างแม่นยำเพียงใด - person supercat; 03.04.2020
comment
ขั้นแรกจะแปลง int 0 เป็นตัวชี้ ตัวชี้ค่าว่าง จากนั้นจึงกำหนด กำหนดไว้อย่างดี อันที่ 2 แปลงประเภทใดก็ตาม NULL เป็นตัวชี้ ตัวชี้ค่าว่าง จากนั้นจึงกำหนด กำหนดไว้อย่างดีด้วย ใช้อย่างใดอย่างหนึ่งดีที่สุดในการเขียนโค้ดตามมาตรฐาน/สไตล์การเขียนโค้ดของกลุ่มของคุณ - person chux - Reinstate Monica; 03.04.2020
comment
@supercat การใช้งานอาจหรืออาจจะไม่รับประกันอะไรเกี่ยวกับตัวอย่าง printf ทั้งหมด --› อะไรไม่รับประกันเกี่ยวกับ printf("%d\n", 0); - person chux - Reinstate Monica; 03.04.2020
comment
@chux-ReinstateMonica: พวกเขาจะต้องเสนอการรับประกันเกี่ยวกับบางส่วน แต่ไม่ใช่ทั้งหมด - person supercat; 03.04.2020
comment
@supercat ฉันหวังว่าเราจะยอมรับว่าตัวที่ 2 และ 3 เป็น UB ที่มีศักยภาพ เนื่องจากความเป็นไปได้ที่ตัวระบุและประเภทอาร์กิวเมนต์อาจไม่ตรงกัน - person chux - Reinstate Monica; 03.04.2020
comment
@ chux-ReinstateMonica: แต่ละรายการจะเป็น UB ในการใช้งานบางอย่าง มาตรฐานขาดคำศัพท์เฉพาะทางในการอธิบายการกระทำซึ่งอาจเป็น UB หรือไม่ ขึ้นอยู่กับลักษณะที่กำหนดการนำไปปฏิบัติ; หากจัดประเภทการกระทำดังกล่าวทั้งหมด ก็มีแนวโน้มที่จะจัดประเภทเป็น UB โดยไม่คำนึงถึงว่าการใช้งานบางอย่างควรกำหนดหรือไม่ [ตัวอย่างเช่น C99 น่าจะมีจุดประสงค์เพียงเพื่อกำจัดข้อกำหนดด้านพฤติกรรมของ C89 สำหรับตัวเลขลบที่เลื่อนไปทางซ้ายในการใช้งานขนาดเครื่องหมาย ซึ่งน่าจะสมเหตุสมผล แต่ต้องใช้พฤติกรรมที่กำหนดไว้บนทุกแพลตฟอร์มใน UB บนทุกแพลตฟอร์ม - person supercat; 03.04.2020