2011-08-20 30 views
6

Nếu tôi không thực sự truy cập vào "đối tượng" bị bỏ rơi, đó là dereferencing con trỏ null vẫn chưa được xác định?Tại điểm nào dereferencing con trỏ null trở thành hành vi không xác định?

int* p = 0; 
int& r = *p; // undefined? 
int* q = &*p; // undefined? 

Ví dụ thực tế hơn một chút: tôi có thể coi trọng con trỏ rỗng để phân biệt giữa quá tải không?

void foo(Bar&); 
void foo(Baz&); 

foo(*(Bar*)0); // undefined? 

Được rồi, các ví dụ tham khảo là hành vi chắc chắn không xác định theo tiêu chuẩn:

một tham chiếu null không thể tồn tại trong một chương trình được xác định rõ, bởi vì cách duy nhất để tạo ra như một tài liệu tham khảo sẽ được ràng buộc nó vào "đối tượng" thu được bởi dereferencing một con trỏ null, gây ra hành vi không xác định.

Thật không may, phần được nhấn mạnh là mơ hồ. Có phải phần ràng buộc gây ra hành vi không xác định hoặc là phần hội nghị dereferencing đủ?

+0

[Câu hỏi này bao gồm thông tin bạn muốn.] (Http://stackoverflow.com/questions/2474018/when-does-invoking-a-member-function-on-a-null-instance-result-in- undefined-behav) – GManNickG

+0

Đây phải là một bản dupe chính xác. Tôi biết chúng tôi đã thảo luận về điều này. –

Trả lời

2

Có hành vi không xác định, vì thông số này cho biết "giá trị chỉ định một đối tượng hoặc hàm" (tại khoản 3.10) và nó cho số * -operator "kết quả [của dereferencing] là một giá trị tham chiếu đến đối tượng hoặc hàm mà biểu thức trỏ tới "(tại khoản 5.3.1).

Điều đó có nghĩa là không có mô tả về những gì sẽ xảy ra khi bạn coi trọng con trỏ rỗng. Nó chỉ đơn giản là hành vi không xác định.

+0

(Nó đã được thảo luận trong các câu hỏi khác về SO. Vì vậy, tôi nghĩ rằng tôi không phải xây dựng trên nó một lần nữa). –

5
int& r = *p; // undefined? 

Tôi nghĩ rằng ngay tại đây bạn đã xác định hành vi ngay cả khi bạn không thực sự sử dụng r (hoặc *p) - đối tượng dereferenced. Bởi vì sau bước này (tức là dereferencing con trỏ null), hành vi của chương trình không được bảo đảm bởi ngôn ngữ, vì chương trình có thể sụp đổ ngay lập tức, đó là một trong những khả năng của UB. Bạn dường như nghĩ rằng chỉ đọc giá trị của r để được sử dụng trong thực tế mục đích gọi UB. Tôi không nghĩ vậy.

Ngoài ra, đặc tả ngôn ngữ nói rõ ràng "tác động của việc hủy bỏ con trỏ rỗng" gọi hành vi không xác định. Nó không không nói "hiệu ứng của thực sự sử dụng đối tượng không tham chiếu từ con trỏ rỗng" gọi UB. Ảnh hưởng của dereferencing con trỏ null (hoặc nói cách khác là hành vi không xác định) không có nghĩa là bạn sẽ nhất thiết và ngay lập tức gặp sự cố hoặc phải báo cáo ngay lập tức sau khi hủy bỏ con trỏ null. Không. Đơn giản là, hành vi của chương trình không được định nghĩa sau khi bỏ qua con trỏ null. Tức là, chương trình có thể chạy bình thường, như mong đợi, từ đầu đến cuối. Hoặc nó có thể sụp đổ ngay lập tức, hoặc sau một thời gian - sau vài phút, giờ hoặc ngày. Mọi thứ đều có thể xảy ra bất kỳ lúc nàosau khi bỏ qua con trỏ null.

+0

Tôi không thể tìm thấy "hiệu ứng của dereferencing con trỏ null" trong tiêu chuẩn. Bạn đã tìm thấy nó ở đâu? – fredoverflow

+1

@FredOverflow: §1.9/4 (C++ 03) nói 'Một số hoạt động khác được mô tả trong tiêu chuẩn này là không xác định (ví dụ, hiệu ứng của dereferencing con trỏ null). [Lưu ý: Tiêu chuẩn này không áp dụng các yêu cầu về hành vi của các chương trình có hành vi không xác định. ] ' – Nawaz

+0

FDIS thay thế ví dụ con trỏ null bằng' ví dụ, ảnh hưởng của việc cố gắng sửa đổi một đối tượng const', có thể chỉ ra rằng các quy tắc về dereferencing con trỏ null đã thay đổi. – fredoverflow

4

Tôi nghĩ rằng hình ảnh thứ hai của What every C programmer should know about Undefined Behavior có thể giúp minh họa vấn đề này.

Lấy ví dụ của blog:

void contains_null_check(int *P) { 
    int dead = *P; 
    if (P == 0) 
    return; 
    *P = 4; 
} 

Có thể được tối ưu hóa để (RNCE: Redundant Null Kiểm tra Elimintation):

void contains_null_check_after_RNCE(int *P) { 
    int dead = *P; 
    if (false) // P was dereferenced by this point, so it can't be null 
    return; 
    *P = 4; 
} 

Đó là rẽ tối ưu hóa vào (DCE: Mã Chết Elimination) :

void contains_null_check_after_RNCE_and_DCE(int *P) { 
    //int dead = *P; -- dead store 
    //if (false)  -- unreachable branch 
    // return; 
    *P = 4; 
} 

Như bạn có thể thấy, mặc dù deadkhông bao giờ sử dụng, việc gán int dead = *P đơn giản đã khiến hành vi không xác định bị leo trong chương trình.

Để phân biệt giữa tình trạng quá tải, tôi khuyên bạn nên sử dụng con trỏ (có thể là null) thay vì tạo tham chiếu không đúng cách và tự phơi bày hành vi không xác định.

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