2013-04-20 35 views
5

Vấn đề này làm phiền tôi một lúc. Tôi chưa bao giờ thấy một định nghĩa khác nhau của NULL, nó luôn luônTại sao NULL không được xác định trước bởi trình biên dịch

#define NULL ((void *) 0) 

có bất kỳ kiến ​​trúc nơi NULL được định nghĩa diferently, và nếu như vậy, tại sao các trình biên dịch không khai báo này cho chúng ta?

+0

Bạn có thể sử dụng '0' thay vì' NULL'. – pmg

+0

NULL được định nghĩa trong 'stddef.h', bạn không phải tự định nghĩa nó. – Mat

+0

@pmg 0 không phải là di động nếu câu trả lời cho câu hỏi đầu tiên của tôi là đúng – stdcall

Trả lời

3

C 2011 Standard, online draft

6.3.2.3 Pointers
...
3 Một biểu thức hằng số nguyên với giá trị 0, hoặc như một biểu đúc gõ void *, được gọi là một hằng số null hằng số. 66) Nếu hằng số con trỏ null được chuyển thành kiểu con trỏ , con trỏ kết quả, được gọi là con trỏ rỗng null, được đảm bảo so sánh bất bình đẳng với con trỏ đến bất kỳ đối tượng hoặc chức năng nào.
66) Macro NULL được đặt trong <stddef.h> (và các tiêu đề khác) dưới dạng hằng số con trỏ null; xem 7.19.

Các vĩ môNULLluôn định nghĩa là một biểu thức hằng số không có giá trị; nó có thể là một trần truồng 0 hoặc 0 cast để void *, hoặc một số biểu hiện không thể thiếu khác mà đánh giá đến 0. Theo như mã nguồn bạn là có liên quan, NULL sẽ luôn ước lượng tới 0.

Khi mã đã được dịch, bất kỳ sự xuất hiện của hằng số null con trỏ (0, NULL, vv) sẽ được thay thế bằng bất kỳ kiến ​​trúc cơ bản nào sử dụng cho một con trỏ null, có thể hoặc không thể có giá trị 0.

0

Tôi không biết câu trả lời cho điều này nhưng tôi đoán. Trong C bạn thường làm rất nhiều mallocs, và do đó nhiều thử nghiệm cho con trỏ trả về. Kể từ khi malloc trả về void *, và đặc biệt là (void *) 0 khi thất bại, NULL là một điều natrual để xác định để thử nghiệm thành công malloc. Vì điều này là rất cần thiết, các hàm thư viện khác sử dụng NULL (hoặc (void *) 0) quá, giống như fopen. Trên thực tế, mọi thứ trả về một con trỏ.

Do đó, không có lý do nào xác định điều này ở cấp độ ngôn ngữ - nó chỉ là một giá trị con trỏ đặc biệt có thể được trả về bởi rất nhiều hàm.

1

Trong thời đại đen tối trước ANSI-C cũ K & R C có nhiều triển khai khác nhau trên phần cứng có thể được coi là kỳ quái ngày nay. Đây là trước những ngày của VM khi máy đã rất "thực". Các địa chỉ 0 không chỉ tốt trên các máy này, địa chỉ số 0 có thể phổ biến ... Tôi nghĩ đó là CDC đôi khi lưu trữ hằng số hệ thống bằng không ở mức 0 (và những điều kỳ lạ xảy ra nếu điều này được đặt khác 0).

 if (NULL != ptr)  /* like this */ 
if (ptr)    /* never like this */

Bí quyết là tìm ra địa chỉ mà bạn có thể yên tâm sử dụng để chỉ ra "không có gì" như lưu trữ những thứ ở phần cuối của bộ nhớ cũng đã được phổ biến, trong đó loại trừ khả năng 0xFFFF trên một số kiến ​​trúc. Và những kiến ​​trúc này có xu hướng sử dụng địa chỉ từ hơn là địa chỉ byte.

4

WhozCraig đã viết những nhận xét này thành answer hiện đã bị xóa, nhưng nó có thể được thăng thành câu trả lời đầy đủ (và đó là những gì tôi đã làm ở đây). Ông lưu ý:

Thú vị lưu ý: AS/400 là một nền tảng rất độc đáo nơi bất kỳ phi hợp lệ con trỏ được coi là tương đương với NULL. Các cơ chế mà họ sử dụng để làm điều này chỉ đơn giản là tuyệt vời. "Hợp lệ" theo nghĩa này là bất kỳ con trỏ 128 bit nào (nền tảng sử dụng không gian địa chỉ tuyến tính 128bit cho mọi thứ) chứa một "giá trị" thu được bởi một bộ chỉ lệnh đã được tin cậy. Khó tin tưởng, int *p = (int *)1; if (p) { printf("foo"); } sẽ không in "foo" trên nền tảng đó. Giá trị được gán cho p không phải là nguồn đáng tin cậy và do đó được coi là "không hợp lệ" và do đó tương đương với NULL.

Thật đáng ngạc nhiên khi nó hoạt động như thế nào. Mỗi đoạn 16 byte trong không gian địa chỉ ảo được ánh xạ của một tiến trình có một bit "tương ứng" trong một bitmap toàn bộ quá trình. Tất cả các con trỏ phải nằm trên một trong những ranh giới đoạn này. Nếu bit được "sáng", con trỏ tương ứng được lưu trữ từ một nguồn đáng tin cậy, nếu không nó sẽ không hợp lệ và tương đương với NULL. Các cuộc gọi đến malloc, toán tử con trỏ, vv, tất cả đều được xem xét kỹ lưỡng trong việc xác định xem bit đó có được thắp sáng hay không. Và như bạn có thể tưởng tượng, việc đưa con trỏ vào các cấu trúc mang đến một thế giới hoàn toàn mới bị tổn thương về ý tưởng đóng gói cấu trúc.


này được đánh dấu cộng đồng wiki (nó không phải là câu trả lời của tôi - tôi không nên lấy tín dụng) nhưng nó có thể bị xóa nếu WhozCraig viết câu trả lời của riêng mình.

Điều này cho thấy rằng có những nền tảng thực với các thuộc tính con trỏ thú vị.

Đã có nền tảng nơi #define NULL ((void *)0) không phải là định nghĩa thông thường; trên một số nền tảng, nó có thể chỉ là 0, trên các nền tảng khác, 0L hoặc 0ULL hoặc các giá trị thích hợp khác miễn là trình biên dịch hiểu nó. C++ không thích ((void *)0) như một định nghĩa; các hệ thống mà các tiêu đề tương tác với C++ cũng có thể không sử dụng phiên bản con trỏ void.

Tôi đã học C trên máy nơi đại diện cho địa chỉ char * cho vị trí bộ nhớ đã cho khác với địa chỉ int * cho cùng một vị trí bộ nhớ. Đây là trong những ngày trước void *, nhưng điều đó có nghĩa là bạn phải có malloc() được khai báo chính xác (char *malloc(); - không có nguyên mẫu), và bạn phải gán giá trị trả về cho đúng loại hoặc bạn có các vùng lõi. Hãy biết ơn đối với tiêu chuẩn C (mặc dù máy được đề cập đến, một ICL Perq - phần cứng bị lỗi từ Three Rivers - phần lớn bị thay thế bởi thời gian tiêu chuẩn được xác định).

+2

Tôi không gặp vấn đề gì với việc lưu trú này. Đã chín năm kể từ khi tôi viết mã cho OS/400, và tất cả những gì tôi biết từ đó đã thay đổi kể từ đó, nhưng đó là một quan điểm vô cùng độc đáo về cách họ thực hiện điều này. đủ cho thời gian chính. Chỉ có một hàm "DLL" được gọi gần như là giải trí, vì nó được định nghĩa theo nghĩa đen là một công việc hệ thống. Nền tảng đó chỉ là * tuyệt vời *. – WhozCraig

Các vấn đề liên quan