Sự khác biệt giữa NULL, '\ 0' và 0 là gì?


309

Trong C, có vẻ như là sự khác biệt giữa các giá trị khác nhau của zero - NULL, NUL0.

Tôi biết rằng các ký tự ASCII '0'đánh giá lại để 48hoặc 0x30.

Con NULLtrỏ thường được định nghĩa là:

#define NULL 0

Hoặc là

#define NULL (void *)0

Ngoài ra, có những NULnhân vật '\0'dường 0như cũng đánh giá là tốt.

Có những lúc ba giá trị này không thể bằng nhau?

Điều này cũng đúng trên hệ thống 64 bit?


1
Xem stackoverflow.com/questions/176989/ Khăn để biết thêm thông tin về sự khác biệt giữa 0 và NULL.
David Rodríguez - dribeas

7
Mã định danh NULkhông tồn tại trong ngôn ngữ hoặc thư viện chuẩn C (hoặc trong C ++ theo như tôi biết). Ký tự null đôi khi được gọi là NUL, nhưng nó là C hoặc C ++, nó thường chỉ được gọi là '\0'.
Keith Thompson

Câu trả lời:


351

Lưu ý: Câu trả lời này áp dụng cho ngôn ngữ C, không phải C ++.


Con trỏ Null

Hằng số nguyên 0có nghĩa khác nhau tùy thuộc vào ngữ cảnh mà nó được sử dụng. Trong mọi trường hợp, nó vẫn là một hằng số nguyên với giá trị 0, nó chỉ được mô tả theo các cách khác nhau.

Nếu một con trỏ đang được so sánh với chữ không đổi 0, thì đây là kiểm tra xem con trỏ có phải là con trỏ không. Điều này 0sau đó được gọi là một hằng con trỏ null. Tiêu chuẩn C định nghĩa rằng 0truyền tới loại void *vừa là con trỏ null vừa là con trỏ null.

Ngoài ra, để giúp dễ đọc, macro NULLđược cung cấp trong tệp tiêu đề stddef.h. Tùy thuộc vào trình biên dịch của bạn, nó có thể có thể #undef NULLvà xác định lại nó thành một cái gì đó lập dị.

Do đó, đây là một số cách hợp lệ để kiểm tra con trỏ null:

if (pointer == NULL)

NULLđược định nghĩa để so sánh bằng với một con trỏ null. Đó là việc thực hiện được định nghĩa định nghĩa thực sự NULLlà gì, miễn là nó là một con trỏ null hợp lệ.

if (pointer == 0)

0 là một đại diện khác của hằng số con trỏ null.

if (!pointer)

Câu iflệnh này hoàn toàn kiểm tra "không phải là 0", vì vậy chúng tôi đảo ngược điều đó thành "0".

Sau đây là các cách INVALID để kiểm tra con trỏ null:

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

Đối với trình biên dịch, đây không phải là kiểm tra con trỏ null, mà là kiểm tra đẳng thức trên hai biến. Điều này có thể hoạt động nếu mynull không bao giờ thay đổi mã và tối ưu hóa trình biên dịch liên tục gập 0 thành câu lệnh if, nhưng điều này không được đảm bảo và trình biên dịch phải tạo ra ít nhất một thông báo chẩn đoán (cảnh báo hoặc lỗi) theo Tiêu chuẩn C.

Lưu ý rằng con trỏ null trong ngôn ngữ C là gì. Nó không quan trọng trên kiến ​​trúc cơ bản. Nếu kiến ​​trúc cơ bản có giá trị con trỏ null được xác định là địa chỉ 0xDEADBEEF, thì tùy thuộc vào trình biên dịch để sắp xếp mớ hỗn độn này.

Như vậy, ngay cả trên kiến ​​trúc ngộ nghĩnh này, các cách sau đây vẫn là những cách hợp lệ để kiểm tra con trỏ null:

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

Sau đây là các cách INVALID để kiểm tra con trỏ null:

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

vì những thứ này được xem bởi một trình biên dịch như là so sánh bình thường.

Nhân vật Null

'\0'được định nghĩa là một ký tự null - đó là một ký tự có tất cả các bit được đặt thành 0. Điều này không có gì để làm với con trỏ. Tuy nhiên, bạn có thể thấy một cái gì đó tương tự như mã này:

if (!*string_pointer)

kiểm tra nếu con trỏ chuỗi đang trỏ vào một ký tự null

if (*string_pointer)

kiểm tra nếu con trỏ chuỗi đang trỏ vào một ký tự không null

Đừng nhầm lẫn với con trỏ null. Chỉ vì cách biểu diễn bit là như nhau và điều này cho phép một số trường hợp giao nhau thuận tiện, chúng không thực sự giống nhau.

Ngoài ra, '\0'là (như tất cả các ký tự bằng chữ) là một hằng số nguyên, trong trường hợp này có giá trị bằng không. Vì vậy, '\0'hoàn toàn tương đương với một 0hằng số nguyên không được cung cấp - sự khác biệt duy nhất là ở mục đích mà nó truyền tải tới người đọc con người ("Tôi đang sử dụng điều này như một ký tự null.").

Người giới thiệu

Xem Câu hỏi 5.3 của Câu hỏi thường gặp về comp.lang.c để biết thêm. Xem pdf này cho tiêu chuẩn C. Kiểm tra các phần 6.3.2.3 Con trỏ, đoạn 3.


3
Cảm ơn đã chỉ vào danh sách Câu hỏi thường gặp. Tuy nhiên, xem thêm c-faq.com/null/nullor0.html
Sinan Ünür

4
Không, bạn sẽ không so sánh ptrvới tất cả các bit-zero . Đây không phải là một memcmp, nhưng đây là so sánh sử dụng toán tử dựng sẵn. Một bên là hằng số con trỏ null '\0'và bên còn lại là con trỏ. Cũng như với hai phiên bản khác với NULL0. Ba người đó làm những điều tương tự.
Julian Schaub - litb

6
Bạn đang sử dụng toán tử so sánh dựng sẵn như là một thứ sẽ so sánh các chuỗi bit. Nhưng đó không phải là nó. Nó so sánh hai giá trị, đó là các khái niệm trừu tượng. Vì vậy, một con trỏ null rằng trong nội bộ được biểu diễn như 0xDEADBEEFvẫn là một con trỏ null, không có vấn đề gì vẻ bitstring nó thích, và nó vẫn sẽ so sánh bằng NULL, 0, \0và tất cả các hình thức liên tục con trỏ null khác.
Johannes Schaub - litb

2
Bạn làm cho một điểm tốt về các toán tử so sánh. Tôi chải lên C99. Nó nói "Một biểu thức hằng số nguyên có giá trị 0 hoặc một biểu thức như vậy được chọn để gõ void *, được gọi là hằng số con trỏ null." Nó cũng nói rằng một ký tự bằng chữ là một biểu thức hằng số nguyên. Như vậy, bởi tính chất bắc cầu bạn đúng đó ptr == '\0'.
Andrew Keeton

2
".... có thể #undef NULL và xác định lại nó thành một thứ gì đó kỳ quặc. Bất cứ ai làm điều này đều xứng đáng bị bắn." điều này thưa ông tốt của tôi đã làm tôi cười thành tiếng ...
oggiemc

34

Dường như một số người hiểu sai về sự khác biệt giữa NULL, '\ 0' và 0 là gì. Vì vậy, để giải thích và trong nỗ lực tránh lặp lại những điều đã nói trước đó:

Một biểu thức không đổi của loại intcó giá trị 0 hoặc biểu thức của loại này, được đúc thành loại void *hằng số con trỏ null , nếu được chuyển đổi thành con trỏ sẽ trở thành con trỏ null . Nó được đảm bảo bởi tiêu chuẩn để so sánh không bằng nhau với bất kỳ con trỏ nào với bất kỳ đối tượng hoặc chức năng nào .

NULLlà một macro, được định nghĩa là hằng số con trỏ null .

\0là một cấu trúc được sử dụng để thể hiện ký tự null , được sử dụng để chấm dứt một chuỗi.

Một ký tự null là một byte có tất cả các bit của nó được đặt thành 0.


14

Tất cả ba xác định ý nghĩa của số không trong bối cảnh khác nhau.

  • bối cảnh con trỏ - NULL được sử dụng và có nghĩa là giá trị của con trỏ là 0, không phụ thuộc vào việc nó là 32 bit hay 64 bit (một trường hợp 4 byte 8 byte khác của số 0).
  • bối cảnh chuỗi - ký tự đại diện cho chữ số 0 có giá trị hex là 0x30, trong khi ký tự NUL có giá trị hex là 0x00 (được sử dụng để kết thúc chuỗi).

Ba cái này luôn khác nhau khi bạn nhìn vào bộ nhớ:

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

Tôi hy vọng điều này làm rõ nó.


8
Nasko: Đánh giá sizeof('\0')và ngạc nhiên.
phê

3
@Nasko: Tôi thực sự ngạc nhiên: với gcc, trong C: sizeof ('\ 0') == sizeof ('a') == 4, trong khi với g ++, trong C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - dribeas

1
@Nasko: Từ tiêu chuẩn C (bản nháp, n1124): 'Hằng số ký tự nguyên có kiểu int', do đó '\ 0' thực sự là kiểu int trong C, và do đó sizeof ('\ 0') là 4 trong kiến ​​trúc của tôi (linux, 32 bit)
David Rodríguez - dribeas

@dribeas - Tôi không mô tả nó như một hằng số, thay vào đó là những gì bạn sẽ thấy như là một phần của chuỗi. Tôi chắc chắn có thể đã làm cho nó rõ ràng. Cảm ơn
Nasko

@ DavidRodríguez-dribeas Hoàn thành chỉnh sửa "Đã sửa giá trị ASCII '0' thành 0x20 (ngày 32 tháng 12)"
chux - Phục hồi Monica

6

Nếu NULL và 0 tương đương với hằng số con trỏ null, tôi nên sử dụng cái nào? trong danh sách C FAQ cũng đề cập đến vấn đề này:

Các lập trình viên C phải hiểu điều đó NULL0có thể hoán đổi cho nhau trong bối cảnh con trỏ, và việc uncast 0 là hoàn toàn chấp nhận được. Bất kỳ việc sử dụng NULL (trái ngược với 0) nên được coi là một lời nhắc nhở nhẹ nhàng rằng một con trỏ có liên quan; lập trình viên không nên phụ thuộc vào nó (vì sự hiểu biết của chính họ hoặc của người biên dịch) để phân biệt con trỏ 0với số nguyên 0.

Nó chỉ trong bối cảnh con trỏ NULL0tương đương. NULLkhông nên được sử dụng khi một loại khác 0được yêu cầu, mặc dù nó có thể hoạt động, bởi vì làm như vậy sẽ gửi thông điệp phong cách sai. (Hơn nữa, ANSI cho phép định nghĩa về NULLđược ((void *)0), mà sẽ không làm việc ở tất cả trong bối cảnh không con trỏ.) Đặc biệt, không sử dụng NULLkhi ký tự ASCII null ( NUL) là mong muốn. Cung cấp định nghĩa của riêng bạn

#define NUL '\0'

nếu bạn phải.


5

Sự khác biệt giữa NULL, '\ 0' và 0 là gì

"null character (NUL)" là dễ loại trừ nhất. '\0'là một nhân vật theo nghĩa đen. Trong C, nó được triển khai như là int, vì vậy, nó giống như 0, là của INT_TYPE_SIZE. Trong C ++, ký tự bằng chữ được triển khai dưới dạng char1 byte. Điều này thường khác với NULLhoặc 0.

Tiếp theo, NULLlà một giá trị con trỏ chỉ định rằng một biến không trỏ đến bất kỳ không gian địa chỉ nào. Đặt sang một bên thực tế là nó thường được thực hiện dưới dạng số không, nó phải có khả năng thể hiện không gian địa chỉ đầy đủ của kiến ​​trúc. Do đó, trên kiến ​​trúc 32 bit, NULL (có khả năng) là 4 byte và trên kiến ​​trúc 64 bit 8 byte. Điều này phụ thuộc vào việc thực hiện C.

Cuối cùng, nghĩa đen 0là loại int, có kích thước INT_TYPE_SIZE. Giá trị mặc định INT_TYPE_SIZEcó thể khác nhau tùy theo kiến ​​trúc.

Apple đã viết:

Mô hình dữ liệu 64 bit được sử dụng bởi Mac OS X được gọi là "LP64". Đây là mô hình dữ liệu phổ biến được sử dụng bởi các hệ thống UNIX 64 bit khác từ Sun và SGI cũng như Linux 64 bit. Mô hình dữ liệu LP64 định nghĩa các kiểu nguyên thủy như sau:

  • số nguyên là 32 bit
  • dài là 64-bit
  • dài cũng là 64-bit
  • con trỏ là 64 bit

Wikipedia 64-bit :

Trình biên dịch VC ++ của Microsoft sử dụng mô hình 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       ?

Chỉnh sửa : Thêm nhiều hơn vào các ký tự chữ.

#include <stdio.h>

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

Đoạn mã trên trả về 4 trên gcc và 1 trên g ++.


2
Không, '\0'không có giá trị 1 byte. Đó là một ký tự, là một biểu thức hằng số nguyên - vì vậy nếu có thể nói nó có kích thước thì đó là kích thước của một int(phải có ít nhất 2 byte). Nếu bạn không tin tôi, hãy tự đánh giá sizeof('\0')và xem. '\0', 00x0tất cả đều hoàn toàn tương đương.
phê

@caf nó phụ thuộc vào ngôn ngữ. Nếu bạn không tin tôi, hãy thử sizeof('\0')trình biên dịch C ++.
Eugene Yokota

2
bạn nên sử dụng "% zu" khi in sizeof (thứ gì đó)
Không sử dụng


4

Một phần tốt giúp tôi khi bắt đầu với C (Lấy từ Lập trình Expert C của Linden)

Một 'l' nul và Hai 'l' null

Ghi nhớ vần nhỏ này để nhớ lại thuật ngữ chính xác cho con trỏ và ASCII zero:

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. 

Ký tự ASCII có mẫu bit bằng 0 được gọi là "NUL". Giá trị con trỏ đặc biệt có nghĩa là điểm con trỏ không ở đâu là "NULL". Hai thuật ngữ không thể thay thế cho nhau.


Nhiều đơn giản: NULlà một mã kiểm soát như BEL, VT, HT, SOTvv và do đó có max. 3 ký tự.
glglgl

2

"NUL" không phải là 0, nhưng đề cập đến ký tự ASCII NUL. Ít nhất, đó là cách tôi đã thấy nó được sử dụng. Con trỏ null thường được định nghĩa là 0, nhưng điều này phụ thuộc vào môi trường bạn đang chạy và đặc điểm kỹ thuật của bất kỳ hệ điều hành hoặc ngôn ngữ nào bạn đang sử dụng.

Trong ANSI C, con trỏ null được chỉ định là giá trị nguyên 0. Vì vậy, bất kỳ thế giới nào không đúng đều không tuân thủ ANSI C.


1

Một byte có giá trị 0x00là, trên bảng ASCII, ký tự đặc biệt được gọi là NULhoặc NULL. Trong C, vì bạn không nên nhúng các ký tự điều khiển trong mã nguồn của mình, điều này được thể hiện trong các chuỗi C có 0 thoát, nghĩa là , \0.

Nhưng một NULL thực sự không phải là một giá trị. Đó là sự vắng mặt của một giá trị. Đối với một con trỏ, nó có nghĩa là con trỏ không có gì để trỏ đến. Trong cơ sở dữ liệu, điều đó có nghĩa là không có giá trị trong một trường (điều này không giống với việc nói trường đó trống, 0 hoặc chứa đầy khoảng trắng).

Các thực tế giá trị của một hệ thống nhất định hoặc định dạng tập tin cơ sở dữ liệu sử dụng để đại diện cho một NULLkhông nhất thiết 0x00.


0

NULLkhông được đảm bảo là 0 - giá trị chính xác của nó phụ thuộc vào kiến ​​trúc. Hầu hết các kiến ​​trúc chính định nghĩa nó (void*)0.

'\0' sẽ luôn bằng 0, vì đó là cách byte 0 được mã hóa theo một ký tự.

Tôi không nhớ liệu trình biên dịch C được yêu cầu phải sử dụng ASCII - nếu không, '0'có thể không luôn luôn bằng 48. Bất kể, chắc bao giờ bạn sẽ gặp phải một hệ thống trong đó sử dụng một bộ ký tự thay thế như EBCDIC trừ khi bạn đang làm việc trên rất hệ thống tối nghĩa.

Kích thước của các loại khác nhau sẽ khác nhau trên các hệ thống 64 bit, nhưng các giá trị nguyên sẽ giống nhau.


Một số bình luận đã bày tỏ nghi ngờ rằng NULL bằng 0, nhưng không phải bằng không. Đây là một chương trình ví dụ, cùng với đầu ra dự kiến ​​trên một hệ thống như vậy:

#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;
}

Chương trình đó có thể in:

NULL == 0
NULL = 0x00000001

2
OP đã hỏi về '\ 0' (ký tự NUL), không phải '0' (ký tự số 0)
Chris Lutz

2
@Chris: '\ 0' không phải là NULL, đó là byte 0 được mã hóa theo số bát phân theo nghĩa đen.
John Millikin

2
Trong C ++, tiêu chuẩn đảm bảo rằng việc chuyển đổi từ giá trị nguyên 0 sang một con trỏ sẽ luôn mang lại một con trỏ null. Trong C ++, 0 được đảm bảo là con trỏ null, trong khi đó, NULL là một macro và một bộ mã hóa độc hại có thể định nghĩa lại nó là một cái gì đó khác.
David Rodríguez - dribeas

6
Và NULL được đảm bảo là 0. Mô hình bit của con trỏ NULL không được đảm bảo là tất cả các số 0, nhưng hằng số NULL là, và sẽ luôn là 0.
jalf

2
Câu đầu tiên của bạn sai - NULL không thể được định nghĩa là (void *) 0 trong C ++ vì không có chuyển đổi ngầm định từ void * sang một con trỏ khác (không giống như trong C).

-2

(void *) 0 là NULL và '\ 0' đại diện cho phần cuối của chuỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.