ฉันสามารถใช้ NULL แทนค่า 0 ได้หรือไม่?


73

ฉันได้รับอนุญาตให้ใช้NULLตัวชี้แทนค่า0ใช่หรือไม่

หรือมีอะไรผิดปกติเกี่ยวกับการทำเช่นนั้น?


ตัวอย่างเช่น:

int i = NULL;

เพื่อทดแทนสำหรับ:

int i = 0;

ในการทดลองฉันรวบรวมรหัสต่อไปนี้:

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

เอาท์พุท:

0

อันที่จริงมันทำให้ฉันเตือนนี้ซึ่งถูกต้องสมบูรณ์ด้วยตัวเอง:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

แต่ผลลัพธ์ยังคงเท่าเดิม


  • ฉันกำลังเข้าสู่ "พฤติกรรมที่ไม่ได้กำหนด" กับสิ่งนี้หรือไม่?
  • อนุญาตให้ใช้NULLในลักษณะนี้ได้ไหม
  • มีอะไรผิดปกติNULLไหมในการใช้เป็นค่าตัวเลขในนิพจน์เชิงคณิตศาสตร์?
  • และผลลัพธ์และพฤติกรรมใน C ++ สำหรับกรณีนี้คืออะไร

ฉันได้อ่านคำตอบของอะไรคือความแตกต่างระหว่างโมฆะ '\ 0' และ 0เกี่ยวกับสิ่งที่แตกต่างระหว่างNULL, \0และ0เป็น แต่ฉันไม่ได้รับข้อมูลที่รัดกุมจากที่นั่นถ้ามันเป็นที่อนุญาตมากและยังมีสิทธิที่จะใช้NULLเป็น ค่าที่จะดำเนินการด้วยในการมอบหมายและการดำเนินการเกี่ยวกับคณิตศาสตร์อื่น ๆ


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
ซามูเอล Liew

มันจะเป็นการดีกว่าถ้าคุณถามคำถามสองข้อแยกต่างหากสำหรับคำถาม C และอีกข้อสำหรับ C ++
Konrad Rudolph

คำตอบ:


82

ฉันได้รับอนุญาตให้ใช้ตัวชี้ NULL แทนค่า 0 หรือไม่

ไม่ไม่ปลอดภัยที่จะทำเช่นนั้น NULLเป็นค่าคงที่ null-pointer ซึ่งอาจมีประเภทintแต่โดยทั่วไปจะมีประเภทvoid *(ใน C) หรืออื่น ๆ ไม่สามารถกำหนดให้โดยตรงกับint(ใน C ++> = 11) ทั้งสองภาษาอนุญาตให้มีการแปลงพอยน์เตอร์เป็นจำนวนเต็ม แต่ไม่ได้จัดเตรียมไว้สำหรับการแปลงดังกล่าวที่จะดำเนินการโดยปริยาย (แม้ว่าคอมไพเลอร์บางตัวจะจัดให้เป็นส่วนขยาย) ยิ่งไปกว่านั้นแม้ว่ามันจะเป็นเรื่องธรรมดาสำหรับการแปลงตัวชี้โมฆะเป็นจำนวนเต็มเพื่อให้ค่า 0, มาตรฐานไม่รับประกันว่า ถ้าคุณต้องการอย่างต่อเนื่องกับชนิดintและความคุ้มค่า 0 0แล้วสะกดมัน

  • ฉันอาจข้ามไปสู่พฤติกรรมที่ไม่ได้กำหนดด้วยสิ่งนี้หรือไม่?

ใช่ในการดำเนินการใด ๆ ที่NULLจะขยายให้เป็นค่าที่มีประเภทvoid *หรืออื่น ๆ intไม่ได้โดยตรงที่จะมอบหมาย มาตรฐานไม่ได้กำหนดพฤติกรรมของการมอบหมายของคุณในการดำเนินการดังกล่าวดังนั้นพฤติกรรมของมันจะไม่ได้กำหนด

  • จะอนุญาตให้ทำงานกับ NULL ในลักษณะนั้นได้หรือไม่?

มันเป็นสไตล์ที่ไม่ดีและมันจะแตกในบางระบบและในบางสถานการณ์ ในขณะที่คุณใช้ GCC มันจะแตกตัวอย่างของคุณเองถ้าคุณคอมไพล์ด้วย-Werrorตัวเลือก

  • มีอะไรผิดปกติเกี่ยวกับการใช้ NULL เป็นค่าตัวเลขในนิพจน์เชิงคณิตศาสตร์หรือไม่?

ใช่. ไม่รับประกันว่าจะมีค่าตัวเลขเลย หากคุณหมายถึง 0 ให้เขียน 0 ซึ่งไม่เพียง แต่นิยามได้ดี แต่สั้นลงและชัดเจนขึ้น

  • และผลลัพธ์ในภาษา C ++ เป็นอย่างไร?

ภาษา C ++ เข้มงวดกับการแปลงมากกว่า C และมีกฎที่แตกต่างกันสำหรับNULLการใช้งานอาจมีส่วนขยายเช่นกัน อีกครั้งถ้าคุณหมายถึง 0 นั่นคือสิ่งที่คุณควรเขียน


4
คุณควรชี้ให้เห็นว่า "ซึ่งโดยทั่วไปแล้วมีประเภทvoid *" เท่านั้นที่เป็นจริงของ C void *ไม่ใช่ประเภทที่ถูกกฎหมายสำหรับ C ++ (เพราะคุณไม่สามารถกำหนดให้void*กับตัวชี้ประเภทอื่น ๆ ได้) ใน C ++ 89 และ C ++ 03 ในความเป็นจริงNULL จะต้องเป็นชนิดintแต่ในรุ่นที่ใหม่กว่าที่จะสามารถ nullptr_t(และมักจะเป็น)
Martin Bonner สนับสนุน Monica

คุณยังไม่ถูกต้องแปลงvoid*ไปintเป็นพฤติกรรมที่ไม่ได้กำหนด มันไม่ใช่; มันเป็นพฤติกรรมที่ระบุการใช้งาน
Martin Bonner สนับสนุน Monica

@MartinBonnersupportsMonica ในบริบทเหล่านั้นที่ C ระบุว่าตัวชี้ถูกแปลงเป็นจำนวนเต็มผลลัพธ์ของการแปลงนั้นเป็นตัวระบุการใช้งานจริง แต่นั่นไม่ใช่สิ่งที่ฉันกำลังพูดถึง มันคือการกำหนดตัวชี้ไปยัง lvalue ประเภทจำนวนเต็ม (โดยไม่ต้องแปลงอย่างชัดเจนผ่านทางนักแสดง) ที่มีพฤติกรรมที่ไม่ได้กำหนด ภาษาไม่ได้กำหนดการแปลงอัตโนมัติที่นั่น
John Bollinger

@MartinBonnersupportsMonica ฉันได้แก้ไขเพื่อให้ครอบคลุมข้อพิจารณา C ++ มากขึ้น ในกรณีใด ๆ ธีมกลางใช้กับทั้งสองภาษาอย่างเท่าเทียมกัน: หากคุณต้องการจำนวนเต็ม 0 จากนั้นเขียนให้ชัดเจนว่าเป็นค่าคงที่จำนวนเต็มประเภทที่เหมาะสม
John Bollinger

31

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

char const* foo = "bar"; 
foo + 0;

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


สำหรับ C ++ สิ่งต่าง ๆ ค่อนข้างแตกต่างกัน การขาดการแปลงโดยปริยายจากvoid*เป็นประเภทวัตถุอื่นหมายถึงที่NULLกำหนดไว้0ในอดีตเป็นในรหัส C ++ ใน C ++ 03 คุณอาจหนีไปได้ แต่เนื่องจากภาษา C ++ 11 ก็สามารถถูกต้องตามกฎหมายได้รับการกำหนดให้เป็นnullptrคำหลัก ตอนนี้สร้างข้อผิดพลาดอีกครั้งเนื่องจากstd::nullptr_tอาจไม่ถูกเพิ่มลงในประเภทตัวชี้

หากNULLถูกกำหนดให้เป็นเช่นnullptrนั้นแม้การทดสอบของคุณจะไม่ถูกต้อง ไม่มีการแปลงจากstd::nullptr_tเป็นจำนวนเต็ม นั่นคือเหตุผลที่มันถูกพิจารณาว่าเป็นตัวชี้โมฆะที่ปลอดภัยกว่า


เพื่อความสมบูรณ์ 0L ยังเป็นค่าคงที่ตัวชี้โมฆะและสามารถใช้ได้NULLทั้งสองภาษา
eerorika

1
@jamesqf มาตรฐานบอกว่าค่าคงที่จำนวนเต็มที่มีค่า 0 เป็นค่าคงที่ตัวชี้โมฆะ ดังนั้น 0L เป็นค่าคงที่ตัวชี้โมฆะ
eerorika

1
@eerorika: สิ่งที่โลกต้องการมาตรฐานที่เพิกเฉยต่อความเป็นจริง :-) 'เพราะถ้าฉันจำชุด 80286 ของฉันได้อย่างถูกต้องคุณไม่สามารถกำหนดตัวชี้ไกลเป็นงานเดี่ยวได้ดังนั้นผู้เขียนคอมไพเลอร์จะต้องพิเศษ - จัดเก็บ
jamesqf

2
@ jamesqf สำหรับคำถามที่พบบ่อย Cอีกครั้ง: การสร้าง0ค่าคงที่ตัวชี้โมฆะ: "เห็นได้ชัดว่าเป็นเสียงร้องของรหัส C ที่เขียนไม่ดีทั้งหมดซึ่งทำให้สมมติฐานที่ไม่ถูกต้อง"
Andrew Henle

3
@jamesqf, ค่าคงที่จำนวนเต็มใด ๆ และทุกค่าที่มีค่า 0 เป็นค่าคงตัว null-pointer (ใน C) ไม่มีส่วนเกี่ยวข้องกับการใช้งานตัวชี้ฮาร์ดแวร์ โปรดทราบว่ามาตรฐาน C ไม่รู้จักความแตกต่างระหว่างตัวชี้ใกล้และไกลในทุกกรณี แต่รองรับการกำหนดตัวชี้แบบตัวชี้ นอกจากนี้ยังสนับสนุนการเปรียบเทียบตัวชี้ (บางส่วน) ซึ่งนำเสนอปัญหาที่น่าสนใจสำหรับรูปแบบการกำหนดแอดเดรสที่แบ่งกลุ่มเช่น 286
John Bollinger

21

ฉันได้รับอนุญาตให้ใช้ตัวชี้ NULL แทนค่า 0 หรือไม่

int i = NULL;

กฎแตกต่างกันระหว่างภาษาและรุ่นของพวกเขา ในบางกรณีคุณสามารถทำได้และในบางกรณีคุณไม่สามารถทำได้ โดยไม่คำนึงถึงคุณไม่ควร หากคุณโชคดีคอมไพเลอร์ของคุณจะเตือนเมื่อคุณพยายามหรือดีกว่าไม่สามารถคอมไพล์

ใน C ++ ก่อน C ++ 11 (อ้างอิงจาก C ++ 03):

[lib.support.types]

NULL เป็นค่าคงที่ตัวชี้ C ++ null ที่ใช้งานซึ่งกำหนดไว้ในมาตรฐานสากลนี้

มันไม่มีเหตุผลที่จะใช้ค่าคงที่ตัวชี้ null เป็นจำนวนเต็ม อย่างไรก็ตาม ...

[conv.ptr]

ค่าคงที่ตัวชี้โมฆะคือนิพจน์ค่าคงที่สำคัญ (5.19) rvalue ของประเภทจำนวนเต็มที่ประเมินค่าเป็นศูนย์

ดังนั้นเทคนิคจะใช้งานได้แม้ว่าจะไร้สาระ NULLเนื่องจากวิชานี้คุณอาจพบโปรแกรมที่เขียนไม่ดีทำผิดกฎ

ตั้งแต่ C ++ 11 (อ้างจากฉบับร่างล่าสุด):

[conv.ptr]

คงชี้โมฆะเป็นตัวอักษรจำนวนเต็ม ([lex.icon]) มีค่าเป็นศูนย์หรือ prvalue ประเภทมาตรฐาน :: nullptr_t

A std​::​nullptr_­tไม่สามารถแปลงเป็นจำนวนเต็มได้ดังนั้นการใช้NULLเป็นจำนวนเต็มจะทำงานได้ตามเงื่อนไขเท่านั้นทั้งนี้ขึ้นอยู่กับตัวเลือกที่เกิดขึ้นจากการนำภาษาไปใช้

PS nullptrเป็น prvalue std​::​nullptr_­tประเภท ถ้าคุณต้องใช้โปรแกรมของคุณเพื่อรวบรวมใน pre-C ++ 11 คุณควรใช้แทนnullptrNULL


C แตกต่างกันเล็กน้อย (เครื่องหมายคำพูดจาก C11 ฉบับร่าง N1548):

6.3.2.3 ภาษา / การแปลง / ตัวถูกดำเนินการอื่น ๆ / พอยน์เตอร์

3 นิพจน์ค่าคงที่จำนวนเต็มที่มีค่า 0 หรือนิพจน์ที่จะพิมพ์void *เรียกว่าค่าคงที่ตัวชี้โมฆะ ...

ดังนั้นเคสจะคล้ายกับโพสต์ C ++ 11 นั่นคือการใช้NULLงานในทางที่ผิดตามเงื่อนไขขึ้นอยู่กับตัวเลือกของการใช้ภาษา


10

ใช่แต่ขึ้นอยู่กับการใช้งานคุณอาจต้องใช้ตัวแปลง แต่ใช่มันถูกต้องตามกฎหมาย 100% มิฉะนั้น

ถึงแม้ว่ามันจะเป็นสไตล์ที่แย่จริงๆ (ไม่จำเป็นต้องพูด?)

NULLคือหรือเป็นจริงไม่ใช่ C ++ มันคือ C อย่างไรก็ตามมาตรฐานทำเช่นเดียวกับมรดก C หลายรายการมีสองส่วน ([diff.null] และ [support.types.nullptr]) ซึ่งทำให้NULLC ++ ทางเทคนิค มันเป็นการดำเนินการกำหนดตัวชี้ null คงที่ ดังนั้นแม้ว่ามันจะเป็นสไตล์ที่ไม่ดีก็ตามมันก็เป็นเทคนิค C ++ เท่าที่จะเป็นไปได้
เป็นแหลมออกในเชิงอรรถการใช้งานที่เป็นไปได้ที่อาจจะ0หรือ0Lแต่ไม่ได้ (void*)0

NULLสามารถของหลักสูตร (มาตรฐานไม่ชัดเจนพูดอย่างนั้น แต่ก็สวยมากทางเลือกเดียวที่เหลืออยู่หลังจาก0หรือ0L) nullptrเป็น นั่นแทบไม่เคยเกิดขึ้น แต่เป็นไปได้ทางกฎหมาย

คำเตือนที่คอมไพเลอร์แสดงให้คุณเห็นว่าคอมไพเลอร์ไม่เข้ากันได้ (เว้นแต่คุณจะรวบรวมในโหมด C) เพราะดีตามคำเตือนมันได้แปลงตัวชี้โมฆะ (ไม่nullptrซึ่งจะเป็นของnullptr_tซึ่งจะแตกต่างกัน) ดังนั้นเห็นได้ชัดว่าคำจำกัดความของNULLเป็นจริง(void*)0ซึ่งมันอาจจะไม่

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

หรือเป็นที่แน่นอนNULL nullptrในกรณีนี้คุณมีค่าที่แตกต่างที่รับประกันเกี่ยวกับการเปรียบเทียบเช่นเดียวกับการแปลงที่กำหนดไว้อย่างชัดเจนจากจำนวนเต็ม แต่ไม่เป็นจำนวนเต็ม อย่างไรก็ตามมีการแปลงที่กำหนดไว้อย่างชัดเจนเป็นbool(ส่งผลให้false) และboolมีการแปลงที่กำหนดไว้อย่างชัดเจนเป็นจำนวนเต็ม (ส่งผลให้0)

โชคไม่ดีที่นั่นเป็นสอง Conversion ดังนั้นจึงไม่ใช่ภายใน"ศูนย์หรือหนึ่ง"ตามที่ระบุใน [Conv] ดังนั้นหากการติดตั้งของคุณNULLเป็นไปตามnullptrนั้นคุณจะต้องเพิ่มตัวแปลงที่ชัดเจนเพื่อให้โค้ดของคุณถูกต้อง


6

จากคำถามที่พบบ่อย C:

ถาม:ถ้าค่า NULL และ 0 เท่ากับค่าคงที่ตัวชี้โมฆะฉันควรใช้แบบใด

ตอบ:มีเฉพาะในบริบทของตัวชี้เท่านั้นNULLและ0เทียบเท่า ไม่NULLควรใช้เมื่อต้องการ 0 ชนิดอื่นแม้ว่าอาจใช้งานได้เนื่องจากการทำเช่นนั้นจะส่งข้อความโวหารที่ไม่ถูกต้อง (นอกจากนี้ ANSI อนุญาตให้นิยามของ NULL เป็นซึ่งจะไม่ทำงานเลยในบริบทที่ไม่ใช่ตัวชี้) โดยเฉพาะอย่าใช้เมื่อต้องการอักขระ ASCII null (NUL) ให้คำจำกัดความของคุณเอง((void *)0)NULL

http://c-faq.com/null/nullor0.html


5

ข้อจำกัดความรับผิดชอบ: ฉันไม่รู้ C ++ คำตอบของฉันไม่ได้มีไว้เพื่อใช้ในบริบทของ C ++

'\0'เป็นintศูนย์ที่มีค่าเท่ากับ 100% เหมือน0กันทุกประการ

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

ในบริบทของพอยน์เตอร์0และNULLเทียบเท่า 100%:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

เทียบเท่าทั้งหมด 100%


หมายเหตุเกี่ยวกับ ptr + NULL

บริบทของการptr + NULLเป็นไม่ได้ว่าของตัวชี้ ไม่มีคำนิยามสำหรับการเพิ่มตัวชี้ในภาษา C; คุณสามารถเพิ่มพอยน์เตอร์และจำนวนเต็ม (หรือลบออก) ในptr + NULLกรณีที่เป็นพอยน์เตอร์ptrหรือNULLเป็นพอยน์เตอร์อื่น ๆจะต้องเป็นจำนวนเต็มดังนั้นจึงptr + NULLมีประสิทธิภาพ(int)ptr + NULLหรือptr + (int)NULLและขึ้นอยู่กับคำจำกัดความของptrและNULLพฤติกรรมหลายอย่างที่สามารถคาดหวัง: มันทำงานได้ทั้งหมดคำเตือนสำหรับการแปลงระหว่างตัวชี้และจำนวนเต็ม .


ฉันเคยเห็น#define NULL (void *)0มาก่อน คุณแน่ใจหรือเปล่าว่า NULL และธรรมดา 0 เทียบเท่า 100%
machine_1

2
ในบริบทของตัวชี้ใช่ ... เงื่อนไขเน้นคำตอบของฉันขอบคุณ
pmg

@phuclv: ฉันไม่รู้อย่างแน่นอนเกี่ยวกับ C ++ คำตอบของฉัน (ยกเว้นบิตระหว่างวงเล็บ) อยู่ที่ประมาณ C
pmg

@phuclv ptr + NULLไม่ได้ใช้NULLในบริบทของพอย
น์เตอร์

3
@JesperJuhl: ในบริบทของพอยน์เตอร์จะเทียบเท่า 100% ฉันไม่รู้ว่าอะไรnullptrคืออะไรแต่((void*)0)และ0(หรือ'\0') เทียบเท่าในบริบทของพอยน์เตอร์ ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg

5

ไม่ไม่ต้องการใช้อีกต่อไปNULL(การเริ่มต้นตัวชี้แบบเก่า)

ตั้งแต่ C ++ 11:

คำสำคัญnullptrหมายถึงตัวอักษรที่แท้จริง เป็น prvalue ของ type std :: nullptr_t มีการแปลงโดยนัยจากnullptrถึงตัวชี้โมฆะของตัวชี้ประเภทใด ๆ และตัวชี้ไปเป็นประเภทสมาชิก แปลงที่คล้ายกันที่มีอยู่สำหรับการใด ๆ คงชี้โมฆะซึ่งรวมถึงค่านิยมของประเภทเช่นเดียวกับแมโครstd::nullptr_tNULL

https://en.cppreference.com/w/cpp/language/nullptr

ที่จริงมาตรฐาน :: nullptr_tnullptrเป็นชนิดของตัวอักษรตัวชี้โมฆะ, มันเป็นประเภทที่แตกต่างที่ไม่ได้เป็นตัวชี้ประเภทหรือตัวชี้ไปยังประเภทสมาชิก

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

เอาท์พุท:

Pointer to integer overload
Pointer to double overload
null pointer overload

คำถามเกี่ยวกับการกำหนดค่า NULL เป็น 0 สำหรับจำนวนเต็ม ในแง่นี้ไม่มีอะไรเปลี่ยนแปลงกับ nullptr แทน NULL
ivan.ukr

เขาใช้คำว่า "ตัวชี้ NULL"
Mannoj

อย่างไรก็ตาม C ++ ไม่มีแนวคิด NULL หลัง C ++ 11 ผู้เขียนอาจสับสนเกี่ยวกับการใช้ constexpr หรือกำหนดค่าเริ่มต้นวิธีเก่า en.cppreference.com/w/cpp/language/default_initialization
Mannoj
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.