ความแตกต่างระหว่าง NULL, '\ 0' และ 0 คืออะไร?


309

ใน C มีปรากฏเป็นความแตกต่างระหว่างค่าต่างๆของศูนย์ - NULL, และNUL0

ฉันรู้ว่าอักขระ ASCII '0'ประเมินหรือ480x30

NULLชี้มักจะถูกกำหนดให้เป็น:

#define NULL 0

หรือ

#define NULL (void *)0

นอกจากนี้ยังมีNULตัวละคร'\0'ที่ดูเหมือนว่าจะประเมิน0เช่นกัน

มีบางครั้งที่ค่าสามค่านี้ไม่สามารถเท่ากันได้หรือไม่?

สิ่งนี้เป็นจริงในระบบ 64 บิตหรือไม่


1
ดูstackoverflow.com/questions/176989/…สำหรับข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่างระหว่าง 0 และ NULL
David Rodríguez - dribeas

7
ตัวระบุNULไม่มีอยู่ในภาษาหรือไลบรารีมาตรฐาน C (หรือใน C ++ เท่าที่ฉันรู้) ตัวละคร null บางครั้งเรียกว่า NUL แต่มัน C หรือ C ++ '\0'มันมักจะเพียงแค่เรียกว่า
Keith Thompson

คำตอบ:


351

หมายเหตุ:คำตอบนี้ใช้กับภาษา C ไม่ใช่ C ++


ตัวชี้ Null

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

หากตัวชี้จะถูกเปรียบเทียบกับตัวอักษรคงที่0นี่คือการตรวจสอบเพื่อดูว่าตัวชี้เป็นตัวชี้โมฆะ นี่0จะเรียกว่าค่าคงที่ตัวชี้โมฆะ มาตรฐาน C กำหนดที่0ส่งไปยังประเภทที่void *เป็นทั้งตัวชี้โมฆะและค่าคงที่ตัวชี้โมฆะ

นอกจากจะช่วยให้การอ่านแมโครที่ระบุไว้ในส่วนหัวของแฟ้มNULL stddef.hขึ้นอยู่กับคอมไพเลอร์ของคุณอาจเป็นไปได้#undef NULLและกำหนดใหม่เป็นสิ่งที่แปลกประหลาด

ดังนั้นนี่คือวิธีที่ถูกต้องในการตรวจสอบตัวชี้โมฆะ:

if (pointer == NULL)

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

if (pointer == 0)

0 เป็นการแทนค่าคงตัวโมฆะตัวอื่น

if (!pointer)

ifคำสั่งนี้ตรวจสอบโดยนัยว่า "ไม่ใช่ 0" ดังนั้นเราจึงย้อนกลับไปที่ความหมาย "เป็น 0"

ต่อไปนี้เป็นวิธีที่ไม่ถูกต้องในการตรวจสอบตัวชี้โมฆะ:

int mynull = 0;
<some code>
if (pointer == mynull)

สำหรับคอมไพเลอร์นี่ไม่ใช่การตรวจสอบตัวชี้โมฆะ แต่เป็นการตรวจสอบความเท่าเทียมกันของตัวแปรสองตัว สิ่งนี้อาจใช้งานได้หาก mynull ไม่เคยเปลี่ยนรหัสและการปรับแต่งคอมไพเลอร์ให้คงที่เท่ากับ 0 ในคำสั่ง if แต่ไม่รับประกันและผู้แปลต้องสร้างข้อความวินิจฉัยอย่างน้อยหนึ่งข้อความ (คำเตือนหรือข้อผิดพลาด) ตามมาตรฐาน C

โปรดทราบว่าตัวชี้ null ในภาษา C คืออะไร มันไม่สำคัญกับสถาปัตยกรรมพื้นฐาน หากสถาปัตยกรรมพื้นฐานมีค่าตัวชี้โมฆะที่กำหนดเป็นที่อยู่ 0xDEADBEEF มันก็ขึ้นอยู่กับคอมไพเลอร์เพื่อเรียงลำดับความยุ่งเหยิงนี้

ดังนั้นแม้จะเป็นสถาปัตยกรรมที่ตลกวิธีการต่อไปนี้ยังคงเป็นวิธีที่ถูกต้องในการตรวจสอบตัวชี้โมฆะ:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

ต่อไปนี้เป็นวิธีที่ไม่ถูกต้องในการตรวจสอบตัวชี้โมฆะ:

#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

คอมไพเลอร์มองว่าเป็นการเปรียบเทียบปกติ

ตัวละคร Null

'\0'ถูกกำหนดให้เป็นอักขระ null - นั่นคืออักขระที่มีบิตทั้งหมดตั้งค่าเป็นศูนย์ สิ่งนี้ไม่เกี่ยวกับพอยน์เตอร์ อย่างไรก็ตามคุณอาจเห็นบางสิ่งที่คล้ายกับรหัสนี้:

if (!*string_pointer)

ตรวจสอบว่าตัวชี้สตริงที่ชี้ไปที่ตัวละคร null

if (*string_pointer)

ตรวจสอบว่าตัวชี้สตริงที่ชี้ไปที่ตัวละครที่ไม่ใช่โมฆะ

อย่าสับสนกับพอยน์เตอร์พอยน์เตอร์เหล่านี้ เพียงเพราะการแสดงบิตเหมือนกันและสิ่งนี้ช่วยให้สะดวกในการข้ามกรณีพวกเขาไม่เหมือนกันจริงๆ

นอกจากนี้'\0'คือ (เช่นตัวอักษรตัวอักษรทั้งหมด) ค่าคงที่จำนวนเต็มในกรณีนี้มีค่าเป็นศูนย์ ดังนั้น'\0'จะเทียบเท่ากับ0ค่าคงที่จำนวนเต็มที่ไม่มีการตกแต่ง- ความแตกต่างเพียงอย่างเดียวคือเจตนาที่สื่อถึงผู้อ่านที่เป็นมนุษย์

อ้างอิง

ดูคำถามที่ 5.3 ของ comp.lang.c คำถามที่พบบ่อยมาก ดูpdf นี้สำหรับมาตรฐาน C ดูที่หัวข้อ 6.3.2.3 พอยน์เตอร์, ย่อหน้าที่ 3.


3
ขอบคุณที่ชี้ไปยังรายการคำถามที่พบบ่อย อย่างไรก็ตามโปรดดูc-faq.com/null/nullor0.html
Sinan Ünür

4
ไม่มีคุณจะไม่เปรียบเทียบptrเพื่อทุกบิตเป็นศูนย์ นี่ไม่ใช่memcmpแต่เป็นการเปรียบเทียบโดยใช้โอเปอเรเตอร์ในตัว ด้านหนึ่งเป็นค่าคงที่ตัวชี้โมฆะ'\0'และอีกด้านหนึ่งเป็นตัวชี้ เช่นกันเช่นเดียวกับอีกสองรุ่นที่มีและNULL 0ทั้งสามคนทำสิ่งเดียวกัน
Johannes Schaub - litb

6
คุณกำลังใช้ตัวดำเนินการเปรียบเทียบ builtin เป็นสิ่งที่จะเปรียบเทียบบิตสตริง แต่นั่นไม่ใช่สิ่งที่มันเป็น มันเปรียบเทียบสองค่าซึ่งเป็นแนวคิดที่เป็นนามธรรม ดังนั้นตัวชี้โมฆะภายในที่แสดงเป็น0xDEADBEEFยังคงเป็นตัวชี้โมฆะว่าสิ่งที่มีลักษณะ bitstring ของมันที่ไม่เหมือนใครและมันจะยังคงเปรียบเทียบเท่ากับNULL, 0, \0และทุกรูปแบบคงที่ชี้โมฆะอื่น ๆ
Johannes Schaub - litb

2
คุณสร้างจุดที่ดีเกี่ยวกับตัวดำเนินการเปรียบเทียบ ฉันแปรงที่ C99 มันบอกว่า "นิพจน์ค่าคงที่จำนวนเต็มที่มีค่า 0 หรือนิพจน์ดังกล่าวเพื่อพิมพ์ void * เรียกว่าค่าคงที่ตัวชี้โมฆะ" นอกจากนี้ยังบอกว่าตัวอักษรตัวอักษรคือการแสดงออกคงที่จำนวนเต็ม ptr == '\0'ดังนั้นโดยทรัพย์สินสกรรมกริยาคุณขวาว่า
Andrew Keeton

2
".... อาจเป็นไปได้ที่จะ #undef NULL และกำหนดใหม่เป็นสิ่งที่แปลกประหลาดใครก็ตามที่ทำสิ่งนี้สมควรถูกยิง" นี่เป็นคนที่ดีของฉันทำให้ฉันหัวเราะออกมาดัง ๆ ...
oggiemc

34

ดูเหมือนว่าผู้คนจำนวนมากเข้าใจผิดว่าความแตกต่างระหว่าง NULL, '\ 0' และ 0 คืออะไร ดังนั้นเพื่ออธิบายและพยายามหลีกเลี่ยงสิ่งที่กล่าวซ้ำก่อนหน้านี้:

แสดงออกอย่างต่อเนื่องของประเภทintที่มีค่าเป็น 0 หรือการแสดงออกของประเภทนี้หล่อชนิดvoid *เป็นอย่างต่อเนื่องชี้โมฆะซึ่งถ้าแปลงเป็นตัวชี้จะกลายเป็นตัวชี้โมฆะ มันมีการประกันโดยมาตรฐานในการเปรียบเทียบที่ไม่เท่าเทียมกันที่จะชี้ใด ๆ กับวัตถุใด ๆ หรือฟังก์ชั่น

NULLเป็นมาโครที่นิยามไว้ในค่าคงตัวชี้ว่าง

\0เป็นสิ่งก่อสร้างที่ใช้แทนอักขระ nullใช้เพื่อยุติสตริง

อักขระ nullเป็นไบต์ซึ่งมีทุกบิตของการตั้งค่าให้เป็น 0


14

ทั้งสามกำหนดความหมายของศูนย์ในบริบทที่แตกต่างกัน

  • บริบทตัวชี้ - ใช้ค่า NULL และหมายถึงค่าของตัวชี้เป็น 0 โดยไม่ขึ้นกับว่าเป็น 32 บิตหรือ 64 บิต (กรณีหนึ่ง 4 ไบต์ไบต์อีก 8 ไบต์เป็นศูนย์)
  • บริบทสตริง - ตัวละครที่เป็นตัวแทนของศูนย์หลักมีค่า hex ของ 0x30 ในขณะที่ตัวละคร NUL มีค่า hex ของ 0x00 (ใช้สำหรับการยุติสตริง)

ทั้งสามนี้แตกต่างกันเสมอเมื่อคุณดูที่หน่วยความจำ:

NULL - 0x00000000 or 0x00000000'00000000 (32 vs 64 bit)
NUL - 0x00 or 0x0000 (ascii vs 2byte unicode)
'0' - 0x20

ฉันหวังว่านี่จะทำให้ชัดเจน


8
Nasko: ประเมินsizeof('\0')และประหลาดใจ
caf

3
@Nasko: ฉันประหลาดใจจริง ๆ : กับ gcc ใน C: sizeof ('\ 0') == sizeof ('a') == 4 ในขณะที่มี g ++ ใน C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - dribeas

1
@Nasko: จากมาตรฐาน C (ฉบับร่าง, n1124): 'ค่าคงที่จำนวนเต็มมี type int' ดังนั้น '\ 0' จึงเป็นประเภท int ใน C และ sizeof ('\ 0') เป็น 4 ในสถาปัตยกรรมของฉัน (linux, 32 บิต)
David Rodríguez - dribeas

@dribeas - ฉันไม่ได้อธิบายว่าเป็นค่าคงที่ แต่สิ่งที่คุณจะเห็นว่าเป็นส่วนหนึ่งของสตริง ฉันสามารถทำให้ชัดเจน ขอบคุณ
Nasko

@ DavidRodríguez-dribeas เลิกแก้ไข "แก้ไขค่า ASCII '0' เป็น 0x20 (ธันวาคม 32)"
chux - Reinstate Monica

6

ถ้า NULL และ 0 เทียบเท่ากับค่าคงที่ตัวชี้ null ฉันควรใช้อะไร ในรายการคำถามที่พบบ่อย C แก้ไขปัญหานี้เช่นกัน:

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

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

#define NUL '\0'

ถ้าคุณต้อง


5

ความแตกต่างระหว่าง NULL คืออะไร '\ 0' และ 0

"null null (NUL)" ง่ายที่สุดในการแยกออก '\0'เป็นตัวอักษรที่แท้จริง ใน C ก็จะถูกนำมาใช้เป็นintดังนั้นก็เป็นเช่นเดียวกับ 0 INT_TYPE_SIZEซึ่งเป็น ใน C ++ ตัวอักษรถูกนำมาใช้เป็นcharซึ่งคือ 1 ไบต์ นี้เป็นปกติแตกต่างจากหรือNULL0

ถัดไปNULLคือค่าตัวชี้ที่ระบุว่าตัวแปรไม่ได้ชี้ไปที่พื้นที่ที่อยู่ใด ๆ แยกแยะข้อเท็จจริงที่ว่ามันถูกนำมาใช้เป็นศูนย์มันจะต้องสามารถแสดงพื้นที่ที่อยู่แบบเต็มของสถาปัตยกรรม ดังนั้นบนสถาปัตยกรรม NULL แบบ 32 บิต (น่าจะเป็น) คือ 4 ไบต์และ 64- บิตสถาปัตยกรรม 8 บิต นี่ขึ้นอยู่กับการใช้งานของ C.

สุดท้ายที่แท้จริง0เป็นชนิดที่มีขนาดint INT_TYPE_SIZEค่าเริ่มต้นของINT_TYPE_SIZEอาจแตกต่างกันไปขึ้นอยู่กับสถาปัตยกรรม

Apple เขียนว่า:

รูปแบบข้อมูล 64 บิตที่ใช้โดย Mac OS X เป็นที่รู้จักกันในชื่อ "LP64" นี่เป็นรูปแบบข้อมูลทั่วไปที่ใช้โดยระบบ UNIX 64 บิตอื่น ๆ จาก Sun และ SGI รวมถึง Linux 64 บิต โมเดลข้อมูล LP64 กำหนดชนิดดั้งเดิมดังนี้:

  • ints คือ 32 บิต
  • ความยาว 64- บิต
  • long-longs เป็น 64- บิตเช่นกัน
  • พอยน์เตอร์เป็น 64- บิต

Wikipedia 64-bit :

คอมไพเลอร์ VC ++ ของ Microsoft ใช้รูปแบบ LLP64

64-bit data models
Data model short int long  long long pointers Sample operating systems
LLP64      16    32  32    64        64       Microsoft Win64 (X64/IA64)
LP64       16    32  64    64        64       Most Unix and Unix-like systems (Solaris, Linux, etc.)
ILP64      16    64  64    64        64       HAL
SILP64     64    64  64    64        64       ?

แก้ไข : เพิ่มเพิ่มเติมเกี่ยวกับตัวอักษร

#include <stdio.h>

int main(void) {
    printf("%d", sizeof('\0'));
    return 0;
}

โค้ดด้านบนส่งคืน 4 บน gcc และ 1 บน g ++


2
ไม่'\0'เป็นไม่ได้ค่า 1 ไบต์ มันเป็นตัวอักษรที่เป็นตัวอักษรซึ่งเป็นค่าคงที่จำนวนเต็ม - ดังนั้นหากสามารถกล่าวได้ว่ามีขนาดขนาดของมันก็คือขนาดของint(ซึ่งต้องมีอย่างน้อย 2 ไบต์) หากคุณไม่เชื่อฉันให้ประเมินsizeof('\0')และดูด้วยตัวคุณเอง '\0', 0และ0x0ทุกคนเทียบเท่าสมบูรณ์
caf

@caf ขึ้นอยู่กับภาษา หากคุณไม่เชื่อฉันลองsizeof('\0')ใช้คอมไพเลอร์ C ++
Eugene Yokota

2
คุณควรใช้ "% zu" เมื่อพิมพ์ขนาด (บางอย่าง)
ไม่ได้ใช้งาน


4

หนึ่งชิ้นที่ดีซึ่งช่วยฉันเมื่อเริ่มต้นด้วย C (นำมาจากการเขียนโปรแกรมผู้เชี่ยวชาญ C โดย Linden)

The One 'nul and the Two' l 'null

จดจำคำคล้องจองเล็กน้อยนี้เพื่อระลึกถึงคำศัพท์ที่ถูกต้องสำหรับพอยน์เตอร์และศูนย์ ASCII:

The one "l" NUL ends an ASCII string,

The two "l" NULL points to no thing.

Apologies to Ogden Nash, but the three "l" nulll means check your spelling. 

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


ง่ายมาก: NULเป็นรหัสควบคุมเช่นBEL, VT, HT, SOTฯลฯ และทำให้มีสูงสุด 3 ตัวอักษร
glglgl

2

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

ใน ANSI C ตัวชี้โมฆะจะถูกระบุเป็นค่าจำนวนเต็ม 0 ดังนั้นโลกที่ไม่เป็นจริงนั้นไม่สอดคล้องกับ ANSI C


1

ไบต์ที่มีค่าของ0x00มีบนโต๊ะ ASCII ตัวละครพิเศษที่เรียกว่าหรือNUL NULLใน C เนื่องจากคุณไม่ควรฝังตัวควบคุมในรหัสที่มาของคุณนี้เป็นตัวแทนในสาย C มีหนี 0 \0คือ

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

จริงค่าระบบหรือรูปแบบไฟล์ฐานข้อมูลได้รับการใช้งานจะเป็นตัวแทนไม่จำเป็นต้องเป็นNULL0x00


0

NULLไม่รับประกันว่าจะเป็น 0 - ค่าที่แน่นอนขึ้นอยู่กับสถาปัตยกรรม (void*)0สถาปัตยกรรมที่สำคัญส่วนใหญ่กำหนดให้มัน

'\0' จะเท่ากับ 0 เสมอเพราะนั่นคือวิธีที่ 0 ไบต์ถูกเข้ารหัสในตัวอักษร

ผมจำไม่ได้ว่า C คอมไพเลอร์จะต้องใช้ ASCII - ถ้าไม่'0'อาจจะไม่เสมอเท่ากับ 48. ไม่คำนึงว่ามันไม่น่าที่คุณจะได้พบระบบที่ใช้ชุดอักขระทางเลือกเช่น EBCDIC ถ้าคุณกำลังทำงานอยู่บนมากระบบที่ไม่ชัดเจน

ขนาดของประเภทต่าง ๆ จะแตกต่างกันไปในระบบ 64 บิต แต่ค่าจำนวนเต็มจะเหมือนกัน


ผู้แสดงความคิดเห็นบางคนแสดงความสงสัยว่าค่า NULL เท่ากับ 0 แต่ไม่เป็นศูนย์ นี่คือตัวอย่างโปรแกรมพร้อมกับเอาต์พุตที่คาดหวังบนระบบดังกล่าว:

#include <stdio.h>

int main () {
    size_t ii;
    int *ptr = NULL;
    unsigned long *null_value = (unsigned long *)&ptr;
    if (NULL == 0) {
        printf ("NULL == 0\n"); }
    printf ("NULL = 0x");
    for (ii = 0; ii < sizeof (ptr); ii++) {
        printf ("%02X", null_value[ii]); }
    printf ("\n");
    return 0;
}

โปรแกรมนั้นสามารถพิมพ์:

NULL == 0
NULL = 0x00000001

2
OP ถูกถามเกี่ยวกับ '\ 0' (ตัวอักษร NUL) ไม่ใช่ '0' (ตัวอักษรศูนย์)
คริสลัทซ์

2
@Chris: '\ 0' ไม่ใช่ NULL เป็นไบต์ 0 ที่เข้ารหัสในฐานแปดในตัวอักษร
John Millikin

2
ใน C ++ มาตรฐานรับรองว่าการแปลงจากค่าจำนวนเต็ม 0 เป็นตัวชี้จะให้ตัวชี้โมฆะเสมอ ใน C ++, 0 รับประกันว่าจะเป็นตัวชี้โมฆะในขณะที่ในทางกลับกัน NULL เป็นแมโครและ coder ที่เป็นอันตรายสามารถกำหนดมันเป็นสิ่งที่แตกต่างกัน
David Rodríguez - dribeas

6
และเป็นโมฆะรับประกันได้ว่าจะ 0. รูปแบบบิตของตัวชี้โมฆะไม่รับประกันว่าจะเป็นศูนย์ทั้งหมด แต่คงเป็นโมฆะเป็นและมักจะเป็น 0.
jalf

2
ประโยคแรกของคุณผิด - NULL ไม่สามารถกำหนดเป็น (void *) 0 ใน C ++ เนื่องจากไม่มีการแปลงโดยนัยจาก void * ไปเป็นตัวชี้อื่น (ไม่เหมือนกับใน C)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.