Tôi có thể sử dụng NULL thay thế cho giá trị 0 không?


73

Tôi có được phép sử dụng NULLcon trỏ thay thế cho giá trị của 0không?

Hoặc có điều gì sai về việc đó?


Ví dụ như:

int i = NULL;

thay thế cho:

int i = 0;

Khi thử nghiệm tôi đã biên dịch mã sau đây:

#include <stdio.h>

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

    return 0;
}

Đầu ra:

0

Quả thực nó mang lại cho tôi cảnh báo này, nó hoàn toàn chính xác:

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

nhưng kết quả vẫn tương đương.


  • Tôi có đang vượt qua "Hành vi không xác định" với điều này không?
  • Được phép sử dụng NULLtheo cách này?
  • Có điều gì sai khi sử dụng NULLnhư một giá trị số trong các biểu thức số học?
  • Và kết quả và hành vi trong C ++ cho trường hợp này là gì?

Tôi có đọc câu trả lời của sự khác biệt giữa NULL là gì, '\ 0' và 0 về những gì là sự khác biệt giữa NULL, \00là, nhưng tôi đã không nhận được thông tin chính xác từ đó, nếu nó là khá phép và cũng phải để sử dụng NULLnhư giá trị để hoạt động với trong bài tập và các hoạt động đối xứng khác.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

Sẽ thực sự tốt hơn nếu hỏi hai câu hỏi riêng biệt, một cho C và một cho C ++.
Konrad Rudolph

Câu trả lời:


82

Tôi có được phép sử dụng con trỏ NULL thay thế cho giá trị 0 không?

Không , nó không an toàn để làm như vậy. NULLlà hằng số con trỏ rỗng, có thể có loại int, nhưng loại này thường có loại void *(trong C) hoặc nếu không được gán trực tiếp cho một int(trong C ++> = 11). Cả hai ngôn ngữ đều cho phép con trỏ được chuyển đổi thành số nguyên, nhưng chúng không cung cấp cho các chuyển đổi đó được thực hiện hoàn toàn (mặc dù một số trình biên dịch cung cấp điều đó như một phần mở rộng). Hơn nữa, mặc dù thông thường để chuyển đổi một con trỏ null thành một số nguyên để mang lại giá trị 0, tiêu chuẩn không đảm bảo điều đó. Nếu bạn muốn một hằng có loại intvà giá trị 0 thì hãy đánh vần nó 0.

  • Tôi có thể chuyển sang Hành vi không xác định với điều này không?

Có, trên bất kỳ triển khai nào khi NULLmở rộng thành giá trị với loại void *hoặc bất kỳ giá trị nào khác không được gán trực tiếp int. Tiêu chuẩn không xác định hành vi chuyển nhượng của bạn đối với việc triển khai như vậy, hành vi của nó không được xác định.

  • Có được phép hoạt động với NULL theo cách đó không?

Đó là phong cách kém, và nó sẽ phá vỡ trên một số hệ thống và trong một số trường hợp. Khi bạn sử dụng GCC, nó sẽ phá vỡ ví dụ của bạn nếu bạn biên dịch với -Werrortùy chọn này.

  • Có điều gì sai về việc sử dụng NULL làm giá trị số trong các biểu thức số học không?

Đúng. Nó không được đảm bảo để có một giá trị số ở tất cả. Nếu bạn có nghĩa là 0 thì viết 0, không chỉ được xác định rõ, mà còn ngắn hơn và rõ ràng hơn.

  • Và làm thế nào là kết quả trong C ++ cho trường hợp đó?

Ngôn ngữ C ++ chặt chẽ hơn về chuyển đổi so với C và có các quy tắc khác nhau NULL, nhưng ở đó, việc triển khai cũng có thể cung cấp các tiện ích mở rộng. Một lần nữa, nếu bạn có nghĩa là 0 thì đó là những gì bạn nên viết.


4
Bạn nên chỉ ra rằng "cái nào điển hình hơn có loại void *" chỉ đúng với C. void *không phải là loại hợp pháp cho C ++ (vì bạn không thể gán void*cho bất kỳ loại con trỏ nào khác). Trong C ++ 89 và C ++ 03, trên thực tế NULL phải là loại int, nhưng trong các phiên bản sau, nó có thể (và thường là) nullptr_t.
Martin Bonner hỗ trợ Monica

Bạn cũng sai khi chuyển đổi void*thành inthành vi không xác định. Không phải vậy; đó là thực hiện hành vi được chỉ định.
Martin Bonner hỗ trợ Monica

@MartinBonnersupportsMonica, Trong những bối cảnh mà C chỉ định rằng một con trỏ được chuyển đổi thành một số nguyên, kết quả của chuyển đổi thực sự được chỉ định thực hiện, nhưng đó không phải là điều tôi đang nói. Đó là việc gán một con trỏ cho một giá trị của kiểu số nguyên (không chuyển đổi rõ ràng thông qua một biểu mẫu) có hành vi không xác định. Ngôn ngữ không xác định chuyển đổi tự động ở đó.
John Bollinger

@MartinBonnersupportsMonica, tôi đã chỉnh sửa để bao gồm nhiều hơn các cân nhắc về C ++. Trong mọi trường hợp, chủ đề trung tâm áp dụng như nhau cho cả hai ngôn ngữ: nếu bạn muốn số nguyên 0 thì hãy viết rõ ràng dưới dạng hằng số nguyên có loại thích hợp.
John Bollinger

31

NULLlà một số con trỏ null không đổi. Trong C, nó có thể là một biểu thức hằng số nguyên có giá trị 0hoặc biểu thức như vậy được truyền tới void*, với khả năng sau có nhiều khả năng hơn. Điều đó có nghĩa là bạn không thể giả sử sử dụng NULLthay thế cho nhau bằng không. Ví dụ, trong mẫu mã này

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

Thay thế 0bằng NULLkhông được đảm bảo là một chương trình C hợp lệ, bởi vì việc thêm vào giữa hai con trỏ (không nói đến các loại con trỏ khác nhau) không được xác định. Nó sẽ khiến chẩn đoán được ban hành do vi phạm ràng buộc. Các toán hạng cho phép cộng sẽ không hợp lệ .


Đối với C ++, mọi thứ có phần khác nhau. Thiếu một chuyển đổi ngầm định từ void*các loại đối tượng khác có nghĩa là NULLđã được xác định theo lịch sử như 0trong mã C ++. Trong C ++ 03, bạn có thể thoát khỏi nó. Nhưng vì C ++ 11 nên nó có thể được định nghĩanullptr một cách hợp pháp từ khóa . Bây giờ một lần nữa tạo ra một lỗi, vì std::nullptr_tcó thể không được thêm vào các loại con trỏ.

Nếu NULLđược định nghĩa như vậy nullptrthì ngay cả thử nghiệm của bạn cũng trở nên không hợp lệ. Không có chuyển đổi từ std::nullptr_tmột số nguyên. Đó là lý do tại sao nó được coi là một con trỏ null an toàn hơn.


Để hoàn thiện, 0L cũng là hằng số con trỏ null và có thể được sử dụng như NULLtrong cả hai ngôn ngữ.
eerorika

1
@jamesqf Tiêu chuẩn nói rằng hằng số nguyên có giá trị 0 là hằng số con trỏ rỗng. Do đó 0L là hằng số con trỏ null.
eerorika

1
@eerorika: Đúng là những gì thế giới cần, các tiêu chuẩn bỏ qua thực tế :-) Vì nếu tôi nhớ chính xác lắp ráp 80286 của mình, bạn thậm chí không thể gán một con trỏ xa như một thao tác duy nhất, vì vậy các trình soạn thảo trình biên dịch sẽ phải đặc biệt - đóng nó
jamesqf

2
@jamesqf Theo C FAQ , re: làm cho 0một con trỏ null không đổi: "hiển nhiên là một sop cho tất cả các mã C được viết kém còn tồn tại mà đưa ra các giả định không chính xác"
Andrew Henle

3
@jamesqf, rằng bất kỳ và mọi hằng số nguyên có giá trị 0 là hằng số con trỏ null (trong C) không liên quan gì đến việc triển khai con trỏ phần cứng. Cũng lưu ý rằng tiêu chuẩn C không nhận ra sự khác biệt giữa các con trỏ gần và xa trong mọi trường hợp, nhưng không hỗ trợ các phép gán con trỏ đến con trỏ. Nó cũng hỗ trợ (một số) so sánh con trỏ, đưa ra các vấn đề thú vị cho các định dạng địa chỉ được phân đoạn, chẳng hạn như của 286.
John Bollinger

21

Tôi có được phép sử dụng con trỏ NULL thay thế cho giá trị 0 không?

int i = NULL;

Các quy tắc khác nhau giữa các ngôn ngữ và phiên bản của họ. Trong một số trường hợp bạn có thể và trong những trường hợp khác, bạn không thể. Bất kể, bạn không nên . Nếu bạn may mắn, trình biên dịch của bạn sẽ cảnh báo khi bạn thử nó hoặc thậm chí tốt hơn, không biên dịch được.

Trong C ++, trước C ++ 11 (trích dẫn từ C ++ 03):

[lib.support.types]

NULL là hằng số con trỏ null C ++ được xác định theo triển khai trong Tiêu chuẩn quốc tế này.

Việc sử dụng một con trỏ null là một số nguyên có ý nghĩa rất nhỏ. Tuy nhiên...

[conv.ptr]

Hằng số con trỏ null là một giá trị hằng biểu thức hằng (5.19) của loại số nguyên ước lượng bằng không.

Vì vậy, về mặt kỹ thuật nó sẽ hoạt động ngay cả khi nó vô lý. Do tính kỹ thuật này, bạn có thể gặp phải các chương trình viết kém mà lạm dụng NULL.

Kể từ C ++ 11 (trích dẫn từ dự thảo mới nhất):

[conv.ptr]

Một hằng con trỏ null là một số nguyên chữ ([lex.icon]) với giá trị zero hoặc một prvalue kiểu std :: nullptr_t .

A std​::​nullptr_­tkhông thể chuyển đổi thành một số nguyên, do đó, sử dụng NULLnhư số nguyên sẽ chỉ hoạt động có điều kiện, tùy thuộc vào các lựa chọn được thực hiện bởi ngôn ngữ.

PS nullptrlà một giá trị của loại std​::​nullptr_­t. Trừ khi bạn cần chương trình của bạn để biên dịch trong pre-C ++ 11, bạn nên luôn luôn sử dụng nullptrthay vì NULL.


C là một chút khác nhau (trích dẫn từ dự thảo C11 N1548):

6.3.2.3 Ngôn ngữ / Chuyển đổi / Toán hạng khác / Con trỏ

3 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 gõvoid * , được gọi là hằng số con trỏ null. ...

Vì vậy, trường hợp tương tự như bài C ++ 11 tức là lạm dụng các NULLtác phẩm có điều kiện tùy thuộc vào các lựa chọn của việc thực hiện ngôn ngữ.


10

, mặc dù tùy thuộc vào việc thực hiện, bạn có thể cần một diễn viên. Nhưng có, nó là hợp pháp 100%, nếu không.

Mặc dù đó là phong cách thực sự, thực sự, thực sự xấu (không cần phải nói?).

NULLlà, hoặc, thực ra không phải là C ++, đó là C. Tuy nhiên, tiêu chuẩn này , giống như đối với nhiều di sản C, có hai mệnh đề ([diff.null] và [support.types.nullptr]) về mặt kỹ thuật tạo ra NULLC ++. Nó là một hằng số con trỏ null được định nghĩa thực hiện . Do đó, ngay cả khi nó là phong cách xấu, về mặt kỹ thuật nó có thể là C ++.
Như đã chỉ ra trong phần chú thích , các triển khai có thể có thể 0hoặc 0L, nhưng không (void*)0 .

NULLtất nhiên có thể (tiêu chuẩn không nói rõ ràng như vậy, nhưng đó là lựa chọn duy nhất còn lại sau 0hoặc 0L) nullptr. Điều đó gần như không bao giờ xảy ra, nhưng đó là một khả năng hợp pháp.

Cảnh báo mà trình biên dịch hiển thị cho bạn chứng minh rằng trình biên dịch trên thực tế không tuân thủ (trừ khi bạn biên dịch ở chế độ C). Bởi vì, tốt, theo cảnh báo, nó đã làm chuyển đổi một con trỏ null (không nullptrđó sẽ là của nullptr_t, đó sẽ là khác nhau), do đó dường như định nghĩa của NULLthực sự là (void*)0, mà nó không thể được.

Dù bằng cách nào, bạn có hai trường hợp hợp pháp (tức là trình biên dịch không bị hỏng). Hoặc (trường hợp thực tế), NULLlà một cái gì đó giống như 0hoặc 0L, sau đó bạn có chuyển đổi "không hoặc một" thành số nguyên, và bạn tốt để đi.

Hoặc NULLlà thực sự nullptr. Trong trường hợp đó, bạn có một giá trị riêng biệt đảm bảo về so sánh cũng như chuyển đổi được xác định rõ ràng từ số nguyên, nhưng không may mắn không chuyển sang số nguyên. Tuy nhiên, nó có một chuyển đổi được xác định rõ ràng thành bool(kết quả false) và boolcó một chuyển đổi được xác định rõ ràng thành số nguyên (kết quả là 0).

Thật không may, đó là hai chuyển đổi, vì vậy nó không nằm trong "không hoặc một" như được chỉ ra trong [conv]. Do đó, nếu việc triển khai của bạn xác định NULLnullptr, thì bạn sẽ phải thêm một biểu mẫu rõ ràng để mã của bạn chính xác.


6

Từ faq C:

Q: 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?

A: Nó chỉ trong bối cảnh con trỏ NULL0tương đương. khôngNULL nên được sử dụng khi một loại 0 khác đượ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 NULL , sẽ hoàn toàn không hoạt động trong bối cảnh không phải con trỏ.) Đặc biệt, không sử dụng khi muốn ký tự null ASCII (NUL). Cung cấp định nghĩa của riêng bạn((void *)0)NULL

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


5

Tuyên bố miễn trừ trách nhiệm: Tôi không biết C ++. Câu trả lời của tôi không có nghĩa là được áp dụng trong bối cảnh của C ++

'\0'là một intgiá trị bằng 0, chính xác như 100% 0.

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

Trong bối cảnh của con trỏ , 0NULLlà 100% tương đương:

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

đều tương đương 100%.


Lưu ý về ptr + NULL

Bối cảnh của ptr + NULLkhông của con trỏ. Không có định nghĩa cho việc thêm con trỏ trong ngôn ngữ C; con trỏ và số nguyên có thể được thêm (hoặc trừ). Trong trường ptr + NULLhợp một ptrhoặc NULLlà một con trỏ, thì con kia phải là một số nguyên, do đó, ptr + NULLcó hiệu quả (int)ptr + NULLhoặc ptr + (int)NULLtùy thuộc vào các định nghĩa ptrNULLmột số hành vi có thể được mong đợi: tất cả đều hoạt động, cảnh báo để chuyển đổi giữa con trỏ và số nguyên, không biên dịch, .. .


Tôi đã thấy #define NULL (void *)0trước đây. Bạn có chắc chắn NULL và đồng bằng 0 tương đương 100% không?
máy_1

2
Trong ngữ cảnh của con trỏ, có ... điều kiện được nhấn mạnh trong câu trả lời của tôi, cảm ơn bạn
pmg

@phuclv: Tôi hoàn toàn không biết gì về C ++. Câu trả lời của tôi (ngoại trừ bit giữa dấu ngoặc đơn) là về C
pmg

@phuclv ptr + NULLkhông được sử dụng NULLtrong ngữ cảnh con trỏ
pmg

3
@JesperJuhl: trong bối cảnh con trỏ, chúng tương đương 100%. Tôi không biết đó nullptrlà gì , nhưng ((void*)0)0(hoặc '\0') tương đương trong bối cảnh con trỏ ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg

5

Không, không còn ưu tiên sử dụng NULL(cách khởi tạo con trỏ cũ).

Kể từ C ++ 11:

Các từ khóa nullptrbiểu thị con trỏ bằng chữ. Đây là một giá trị của loại std :: nullptr_t. Có tồn tại chuyển đổi ngầm định từ nullptrgiá trị con trỏ null của bất kỳ loại con trỏ và bất kỳ con trỏ đến loại thành viên. Chuyển đổi tương tự tồn tại cho bất kỳ con trỏ null nào, bao gồm các giá trị của loại std::nullptr_tcũng như macro NULL.

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

Trên thực tế, std :: nullptr_t là loại con trỏ null bằng chữ , nullptr. Nó là một loại khác biệt mà bản thân nó không phải là một loại con trỏ hoặc một con trỏ đến loại thành viên.

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

Đầu ra:

Pointer to integer overload
Pointer to double overload
null pointer overload

Câu hỏi là về việc gán NULL thành 0 cho số nguyên. Theo nghĩa này, không có gì thay đổi với nullptr thay vì NULL.
ivan.ukr

Ông đã sử dụng các từ như "con trỏ NULL".
Mannoj

Nhân tiện, C ++ không có khái niệm NULL sau C ++ 11. Tác giả có thể bị nhầm lẫn về việc sử dụng constexpr hoặc xác định khởi tạo cách cũ. en.cppreference.com/w/cpp/lingu/default_initialization
Mannoj
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.