2009-12-01 38 views
33
try { 
     int* p = 0; 
     *p = 1; 
    } catch (...) { 
     cout << "null pointer." << endl; 
    } 

Tôi đã cố gắng nắm bắt ngoại lệ như thế này nhưng nó không hoạt động, bất kỳ trợ giúp nào?Làm thế nào để bắt ngoại lệ con trỏ null?

+27

Đối với VC++, 'catch (...)' sẽ bắt AV nếu bạn biên dịch bằng 'cl.exe/EHa'. Tuy nhiên, nếu bạn đã từng làm điều này, một vị thần C++ giận dữ sẽ ngay lập tức tấn công bạn bằng tia sét ngay tại chỗ, vì vậy hãy quên tôi đã nói với bạn điều đó. –

+10

Tránh làm hỏng chương trình của tôi hoặc bị sét đánh. Một tình trạng khó xử của lập trình viên tuổi già. –

+3

Đừng nhầm lẫn ngoại lệ C++ với hệ thống "ngoại lệ" của Microsoft. –

Trả lời

50

Không có điều nào như "ngoại lệ con trỏ null" trong C++. Các trường hợp ngoại lệ duy nhất bạn có thể nắm bắt, là ngoại lệ được biểu thị rõ ràng bởi các biểu thức throw (cộng với, như Pavel đã lưu ý, một số ngoại lệ C++ tiêu chuẩn được ném nội tại theo tiêu chuẩn operator new, dynamic_cast v.v.). Không có ngoại lệ nào khác trong C++. Dereferencing null con trỏ, phân chia bằng không vv không tạo ra ngoại lệ trong C + +, nó tạo ra hành vi không xác định. Nếu bạn muốn ngoại lệ được ném trong các trường hợp như vậy, bạn có trách nhiệm tự phát hiện các điều kiện này và thực hiện theo cách thủ công throw. Đó là cách nó hoạt động trong C++.

Bất kỳ điều gì khác mà bạn dường như đang tìm kiếm đều liên quan đến ngôn ngữ C++, nhưng thay vào đó là một tính năng của việc triển khai cụ thể. Trong Visual C++, ví dụ, ngoại lệ hệ thống/phần cứng có thể được "chuyển đổi" thành ngoại lệ C++, nhưng có một mức giá gắn liền với chức năng phi tiêu chuẩn này, thường không đáng trả tiền.

+1

"Ngoại lệ duy nhất bạn có thể nắm bắt, là ngoại lệ được ném một cách rõ ràng bởi biểu thức ném" - và mặc định 'toán tử mới' (nó có thể được thực hiện bằng cách sử dụng' throw' rõ ràng, chắc chắn, nhưng không có gì yêu cầu nó để làm điều đó; cũng là một bản chất). –

+1

Có nhiều vị trí trong C++ có thể ném bên cạnh 'toán tử new',' dynamic_cast' vào kiểu tham chiếu là một ví dụ khác, nhưng đó không phải là điểm. Vấn đề là bạn chỉ có thể bắt được ngoại lệ C++. Tất nhiên, tuyên bố rằng họ chỉ có thể được ném bởi một người dùng cấp 'ném' là không chính xác. – AnT

+0

Dereferencing một con trỏ null/phân chia bằng 0 không tạo ra hành vi không xác định. Nó ném một ngoại lệ, sự khác biệt là nó là một ngoại lệ hệ điều hành, không phải là một C++. – Michael

23

Bạn không thể. De-tham chiếu một con trỏ null là một điều hệ thống.

Trên Linux, hệ điều hành sẽ tăng tín hiệu trong ứng dụng của bạn. Hãy xem csignal để xem cách xử lý tín hiệu. Để "bắt", bạn sẽ móc một hàm trong đó sẽ được gọi trong trường hợp SIGSEGV. Ở đây bạn có thể thử in một số thông tin trước khi bạn chấm dứt chương trình một cách duyên dáng.

Sử dụng Windows structured-exception-handling. Bạn có thể sử dụng công cụ __try/__except, như được nêu trong liên kết trước. Cách tôi đã làm nó trong một tiện ích gỡ lỗi nhất định tôi đã viết là với chức năng _set_se_translator (vì nó phù hợp chặt chẽ với móc). Trong Visual Studio, đảm bảo bạn đã bật SEH. Với hàm đó, bạn có thể móc vào một hàm để gọi khi hệ thống đưa ra một ngoại lệ trong ứng dụng của bạn; trong trường hợp của bạn, nó sẽ gọi nó là EXCEPTION_ACCESS_VIOLATION. Sau đó bạn có thể ném một ngoại lệ và tuyên truyền trở lại như thể một ngoại lệ đã được ném ở nơi đầu tiên.

+0

Bạn cũng phải sử dụng '/ EHa' khi sử dụng' _set_se_translator'. –

+3

Xin chào, tôi đã nói rằng: P – GManNickG

+0

Tôi thấy '_set_se_translator' chỉ sử dụng hạn chế vì (từ msdn) ..." Trong môi trường đa luồng, các chức năng phiên dịch được duy trì riêng cho mỗi luồng. Mỗi chuỗi mới cần cài đặt riêng Vì vậy, mỗi thread chịu trách nhiệm xử lý bản dịch của riêng nó. _set_se_translator là cụ thể cho một luồng; một DLL khác có thể cài đặt một hàm dịch khác. " ... Vì vậy, nếu bạn không kiểm soát việc tạo ra tất cả các chủ đề gọi mã của bạn, bạn không thể sử dụng nó. Có nói rằng boost.test sử dụng này để có hiệu lực tuyệt vời, nhưng đây là đơn luồng – iain

5

C++ không kiểm tra con trỏ (mặc dù tôi cho rằng một số triển khai có thể). Nếu bạn cố gắng ghi vào một con trỏ null thì rất có khả năng nó sẽ bị sập. Nó sẽ không ném một ngoại lệ. Nếu bạn muốn nắm bắt điều này, bạn cần phải kiểm tra giá trị của con trỏ chính mình trước khi bạn cố gắng ghi vào nó.

+5

Nó thực sự phụ thuộc vào việc thực hiện và hệ điều hành. Ví dụ. trên Win32, dereferencing một con trỏ null sẽ dẫn đến vi phạm truy cập (như bất kỳ nền tảng khác với bảo vệ bộ nhớ, thực sự), nhưng Win32 AV là một ngoại lệ có cấu trúc và nó có thể bị bắt bởi trình biên dịch C++. của Win32 SEH - ví dụ, VC++ với cờ biên dịch '/ EHa'. Tất nhiên, nó vẫn còn ác tuyệt đối (và không thể di chuyển). –

+3

Nói về bảo vệ bộ nhớ, tôi bắt đầu viết C trên DOS, khi viết vào một con trỏ null thường có nghĩa là bạn sẽ khởi động lại máy tính của mình. Ít nhất DOS khởi động khá nhanh. –

+0

@Nate C-K: Tôi đã từng làm rối tung một số con trỏ trong một chương trình C trên DOS và trước khi treo máy tính in nội dung của ROM. Hoặc ít nhất đó là cách tôi giải thích những gì tôi nhìn thấy trên màn hình trở lại sau đó. – Giorgio

8

Dereferencing một null (hoặc con trỏ đó là cuối cùng của mảng, hoặc một con trỏ không hợp lệ ngẫu nhiên) kết quả trong hành vi không xác định. Không có cách cầm tay để "bắt" điều đó.

1

Như những người khác đã nói, bạn không thể làm điều này trong C++.

Nếu tôi có thể làm cho một điểm rộng hơn: ngay cả trong một ngôn ngữ cho phép bạn nắm bắt nó, hành động tốt hơn là không chạm vào con trỏ rỗng. Bắt gặp một lỗi khi nó đã bị thổi vào mặt bạn, sau đó quyết định tiếp tục như thể nó không xảy ra, không phải là một chiến lược mã hóa tốt. Những điều như dereference con trỏ null, tràn ngăn xếp, vv, nên được xem là sự kiện thảm khốc và tránh né, ngay cả khi ngôn ngữ của bạn cho phép bạn phản ứng với nó một cách khác nhau.

1

Không có nền tảng độc lập nào để thực hiện việc này. Trong Windows/MSVC++, bạn có thể sử dụng __try/__ ngoại trừ

Nhưng tôi không khuyên bạn nên làm việc đó. Bạn gần như chắc chắn không thể phục hồi chính xác từ một lỗi phân đoạn.

+0

Ông đặc biệt yêu cầu về dereference con trỏ null, mà chắc chắn là khá phục hồi (vì nó không làm hỏng bộ nhớ, vv). Vẫn không phải là một ý tưởng hay, nhưng vì nhiều lý do khác nhau. –

+0

Thật vậy, ngoại lệ duy nhất tôi gặp khó khăn một chút khi khôi phục lại là tràn bộ đệm. Trong thực tế, tôi không thể. Tôi không nghĩ rằng nó có thể, như ném một ngoại lệ sử dụng quá nhiều ngăn xếp, ít nhất là trên Windows (Linux cung cấp một ngăn xếp thay thế cho trường hợp ngoại lệ). Bạn có đủ thời gian để sinh ra một luồng mới và cố gắng tiết kiệm càng nhiều thông tin càng tốt. – GManNickG

+0

Tôi không đồng ý rằng nó có thể phục hồi được, vì nó đại diện cho một lỗi logic trong chương trình, và cách duy nhất để sửa lỗi logic là thay đổi chính chương trình. Nó có thể tra cứu được, nhưng trappable không có nghĩa là có thể phục hồi được. Nếu chương trình của bạn phát hiện một lỗi logic, chẳng hạn như điều này, nó sẽ sụp đổ sớm, tai nạn thường xuyên. – DrPizza

3

Thông thường bạn không thể. Ngay cả khi bạn có thể nó sẽ giống như cố gắng để đưa một viện trợ ban nhạc trên một tàu ngầm đã nổi lên một rò rỉ.

Ứng dụng bị tê liệt có thể gây sát thương nhiều hơn số lần bị hỏng. Lời khuyên của tôi ở đây sẽ là để cho nó sụp đổ sau đó sửa chữa lý do tại sao nó bị rơi. Rửa sạch. Nói lại.

+0

Và cảm ơn thần cho điều này, quá. Điều này thực sự làm cho các lập trình viên tốt hơn và mã tốt hơn tôi nghĩ. –

6

Có một cách rất dễ dàng để bắt bất kỳ loại ngoại lệ (phép chia cho không, vi phạm truy cập, vv) trong Visual Studio sử dụng try ->catch (...) khối.

Điều chỉnh dự án nhỏ là đủ. Chỉ cần bật tùy chọn /EHa trong cài đặt dự án. Xem Thuộc tính dự án -> C/C++ -> Tạo mã -> Sửa đổi Bật ngoại lệ C++ thành "Có với ngoại lệ SEH". Đó là nó!

Xem chi tiết tại đây: http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx

0

Nếu bạn muốn bạn chỉ có thể làm con trỏ kiểm tra bản thân và ném ...

if (p == nullptr) throw std::exception("woot! a nullptr!") 
p->foo(); 

nên khóa học này sẽ chỉ là để gỡ lỗi các vấn đề, các nullptr không nên xảy ra ở nơi đầu tiên :)

0

Trả lời ngắn - bạn không thể theo cách di động hoặc tiêu chuẩn, vì các lỗi như thế này có khả năng làm hỏng chính quá trình đó.

Câu trả lời dài- bạn có thể làm nhiều hơn bạn nghĩ, và chắc chắn nhiều hơn mặc định của chương trình chỉ bị lỗi. Tuy nhiên, bạn cần lưu ý 3 điều sau:
1) Các lỗi này nghiêm trọng hơn ngoại lệ và thường không thể là ngoại lệ đối với logic của bạn.
2) Phát hiện và xử lý thư viện của bạn S W phụ thuộc vào nền tảng ở mặt sau, mặc dù bạn có thể cung cấp giao diện trừu tượng rõ ràng để sử dụng công cộng.
3) Sẽ luôn có một số sự cố tồi tệ đến nỗi bạn thậm chí không thể phát hiện chúng trước khi kết thúc.

Về cơ bản, các lỗi như segfaults hoặc heap corruption không phải là ngoại lệ vì chúng đang làm hỏng quá trình thực tế đang chạy chương trình. Bất kỳ thứ gì bạn đã mã hóa vào chương trình đều là một phần của chương trình, bao gồm xử lý ngoại lệ, vì vậy bất cứ điều gì ngoài việc ghi nhật ký một thông báo lỗi đẹp trước khi quá trình chết là không thể chấp nhận được trong vài trường hợp không phải là không thể. Trong POSIX, hệ điều hành sử dụng hệ thống báo hiệu để báo cáo lỗi như thế này và bạn có thể đăng ký chức năng gọi lại để ghi lại lỗi trước khi bạn thoát. Trong Windows, hệ điều hành đôi khi có thể chuyển đổi chúng thành các ngoại lệ trông bình thường mà bạn có thể nắm bắt và phục hồi.

Cuối cùng, đặt cược tốt nhất của bạn là mã hóa phòng thủ chống lại những cơn ác mộng như vậy. Trên bất kỳ hệ điều hành nào, sẽ có một số hệ điều hành tồi tệ đến mức bạn không thể phát hiện chúng, thậm chí về nguyên tắc, trước khi quá trình của bạn bị chết. Ví dụ, việc làm hỏng con trỏ ngăn xếp của chính bạn là một thứ có thể làm hỏng bạn đến nỗi ngay cả khi các cuộc gọi lại tín hiệu POSIX của bạn không bao giờ nhìn thấy nó.

0

Trong VC++ 2013 (và các phiên bản trước đó cũng) bạn có thể đặt breakpoint trên trường hợp ngoại lệ:

  1. Nhấn Ctrl + Alt + Delete (điều này sẽ mở hộp thoại ngoại lệ).
  2. Mở rộng 'Ngoại lệ Win32'
  3. Đảm bảo rằng ngoại lệ "Vi phạm truy cập 0xC0000005" được chọn.

Bây giờ gỡ lỗi lại, điểm ngắt sẽ được nhấn chính xác khi xảy ra hủy bỏ rỗng.

-1

Không có ngoại lệ con trỏ NULL tồn tại trong c + + nhưng bạn vẫn muốn bắt cùng một lúc thì bạn cần cung cấp triển khai lớp của riêng bạn cho cùng.

dưới đây là ví dụ cho giống nhau.

class Exception { 

public: 
    Exception(const string& msg,int val) : msg_(msg),e(val) {} 
    ~Exception() {} 

    string getMessage() const {return(msg_);} 
    int what(){ return e;} 
private: 
    string msg_; 
    int e; 
}; 

Bây giờ dựa trên con trỏ NULL kiểm tra xem nó có thể được ném như, throw(Exception("NullPointerException",NULL)); và dưới đây là mã cho đánh bắt giống nhau.

catch(Exception& e) { 
     cout << "Not a valid object: " << e.getMessage()<< ": "; 

     cout<<"value="<<e.what()<< endl; 
    } 
Các vấn đề liên quan