2015-12-12 14 views
23

Tôi đã có một câu hỏi như thế này trên một trong các kỳ thi của tôi và tôi vẫn không quá chắc chắn làm thế nào để trả lời nó. Tôi hiểu rằng các xác nhận là cách để kiểm tra chương trình của bạn, tuy nhiên tôi không chắc chắn những gì assert(0) đang kiểm tra. Đây có phải là một câu hỏi lừa? Nó sẽ luôn thất bại, nhưng tôi không hiểu tại sao. Nó đang kiểm tra cái gì?Xác nhận (0) có nghĩa là gì?

Bất kỳ lời giải thích nào cũng tuyệt vời, cảm ơn.

+3

Sử dụng nhiều dấu chấm than: khẳng định (!!! "Điều bạn hy vọng sẽ không xảy ra. Nó vừa xảy ra"); –

+0

Chỉ cần cẩn thận để sử dụng một số lẻ của chúng. Và xem xét nhận xét của Terry Pratchett, "Nhiều dấu chấm than", anh ta tiếp tục, lắc đầu, "là một dấu hiệu chắc chắn của một tâm trí bị bệnh". Và 3 dấu chấm than chắc chắn là "nhiều", giải pháp duy nhất chính xác là 1 Dù sao, tôi nhớ lại rằng Andrei Alexandrescu và tôi đã không đồng ý một cách tốt nhất. Andrei ủng hộ 'khẳng định (" Blah "&& condition)' trong khi tôi ủng hộ 'khẳng định ((" Blah ", điều kiện))' (cả hai chúng ta đều không ủng hộ dấu chấm than). Hôm nay tôi sử dụng '&&', bởi vì nó đơn giản hơn *, dễ dàng hơn. Nhưng đó là lý do tại sao tôi sau đó ủng hộ '(())' ... –

Trả lời

24

Tiêu chuẩn C++ chống định nghĩa assert theo tiêu chuẩn C.

C99 §7.2/2:

Các assert vĩ mô đặt xét nghiệm chẩn đoán vào các chương trình; nó mở rộng thành một biểu thức void. Khi nó được thực hiện, nếu biểu thức (phải có loại vô hướng) là sai (nghĩa là, so sánh bằng 0), macro khẳng định ghi thông tin về cuộc gọi cụ thể không thành công (bao gồm văn bản của đối số, tên của tệp nguồn, số dòng nguồn và tên của hàm kèm theo - sau này tương ứng với các giá trị của các macro tiền xử lý __FILE____LINE__ và số nhận dạng __func__) trên tệp lỗi chuẩn theo định dạng được xác định. Sau đó, nó gọi hàm abort.


Trong assert(0) các 0 được hiểu như là false, vì vậy sự khẳng định này sẽ luôn luôn thất bại, hoặc lửa, khi kiểm tra khẳng định là trên.

Do đó nó khẳng định rằng

“ Việc thực hiện sẽ không bao giờ đạt đến điểm này. ”

Thực tế, khó có thể làm cho trình biên dịch đóng cửa khi thực hiện đạt hoặc không đạt đến điểm đã cho. Thông thường trình biên dịch đầu tiên sẽ phàn nàn về việc thực hiện có thể đạt đến kết thúc của một hàm mà không trả về một giá trị. Thêm assert(0) lý tưởng nên giải quyết vấn đề đó, nhưng sau đó trình biên dịch có thể khiếu nại về số assert hoặc không nhận ra rằng nó cho biết bạn đã biết rõ những gì nó cố cảnh báo.

Một (1) biện pháp có thể sau đó cũng để ném một ngoại lệ tại thời điểm đó:

auto foo(int x) 
    -> int 
{ 
    if(x == 1) { return 42; } 
    assert(0); throw 0;  // Should never get here! 
} 

Tất nhiên rằng đôi whammy có thể được định nghĩa là một cấp độ vĩ mô cao hơn. Về loại ngoại lệ, bạn có thể muốn giữ nó như không phải là std::exception, bởi vì đây không phải là một ngoại lệ có ý định bị bắt bởi một số catch thông thường ở bất kỳ đâu.Hoặc, nếu bạn tin tưởng vào hệ thống phân cấp ngoại lệ tiêu chuẩn (nó không có ý nghĩa với tôi, nhưng) bạn có thể sử dụng một std::logic_error.


Để tắt assert khẳng định việc kiểm tra, bạn có thể xác định biểu tượng NDEBUG trước bao gồm <assert.h>.

Tiêu đề này có hỗ trợ đặc biệt để bạn có thể bao gồm nhiều lần, có hoặc không có NDEBUG được xác định.

C++ 11 §17.6.2.2/2:

Một đơn vị dịch thuật có thể bao gồm tiêu đề thư viện trong bất kỳ thứ tự (khoản 2). Mỗi lần có thể được bao gồm nhiều hơn một lần, không có hiệu lực khác với việc được bao gồm chính xác một lần, ngoại trừ hiệu ứng bao gồm <cassert> hoặc <assert.h> phụ thuộc mỗi lần theo định nghĩa từ vựng hiện tại là NDEBUG.

Định nghĩa hợp lý về tính năng đánh lừa đôi được thảo luận ở trên có thể tương tự như vậy phụ thuộc vào NDEBUG không có bảo vệ, ví dụ:

file assert_should_never_get_here.hpp
#include <stdexcept>  // std::logic_error 
#include <assert.h> 

#undef ASSERT_SHOULD_NEVER_GET_HERE 
#ifdef NDEBUG 
# define ASSERT_SHOULD_NEVER_GET_HERE() \ 
     throw std::logic_error("Reached a supposed unreachable point") 
#else 
# define ASSERT_SHOULD_NEVER_GET_HERE() \ 
     do{ \ 
      assert("Reached a supposed unreachable point" && 0); \ 
      throw 0; \ 
     } while(0) 
#endif 

Disclaimer: Trong khi tôi mã đó lên một số lần trong đầu những năm 2000, tôi nấu chín lên đoạn mã trên chỉ dành riêng cho câu trả lời này, và trong khi tôi đã thử nghiệm nó với g ++ nó có thể không nhất thiết phải hoàn hảo.


(1) Xem Basile Starynkevitch's answer để thảo luận các khả năng khác, g ++ - cụ thể nội tại __builtin_unreachable.

+1

Rất tốt để thông báo khẳng định có thể bị vô hiệu hóa: Lưu lượng mã của bạn phải giống nhau dù có khẳng định hay không! – edmz

12

Nó sẽ luôn thất bại. Nó khá là nhiều. Nó sẽ thất bại luôn vì cùng một lý do rằng "khẳng định (x == 5)" sẽ thành công bất cứ khi nào x = 5.

Nếu bạn yêu cầu một ứng dụng thì bạn sẽ đặt nó trong các khối mã thực sự không xảy ra.

switch(suit) { 
    case CLUB: 
    case DIAMOND: 
    case HEART: 
    case SPADE: 
    // ... 
    default: 
    assert(0); 
} 
+0

Nhưng thường là một khẳng định là một điều kiện, phải không? Vì vậy, nó có nghĩa là gì nếu chỉ có "0"? –

+2

@SmithWestern số không là một điều kiện luôn luôn sai, giống như "thời điểm ngay bây giờ là ngay bây giờ" là một điều kiện luôn luôn đúng. – djechlin

+1

Đó phải là "bất cứ khi nào x! = 5" – immibis

8

Có, nó sẽ luôn thất bại.

assert(0) hoặc assert(false) thường được sử dụng để đánh dấu đang unreachable, do đó trong chế độ gỡ lỗi nhắn chẩn đoán được phát ra và chương trình bị bỏ dở khi được cho là không thể kết nối được thực sự đạt được, đó là một tín hiệu rõ ràng rằng chương trình isn' Làm những gì chúng tôi nghĩ.

+1

Không phải là "không thể truy cập", nhưng "chỉ không thể truy cập nếu có lỗi trong mã của tôi". Không thể truy cập mã không thể truy cập. "chỉ không thể truy cập nếu có lỗi trong mã của tôi" được truy cập quá thường xuyên :-( – gnasher729

3

Trong Ngoài để câu trả lời khác (đặc biệt là this một), nếu bạn đang sử dụng một gần đây GCC (hoặc Clang), bạn có thể xem xét sử dụng một số GCC builtin, đáng chú ý __builtin_unreachable() thay vì assert(0).

Có một số khác biệt: trước tiên, assert có thể bị vô hiệu hóa với -DNDEBUG. Và __builtin_unreachable sẽ thay đổi cách trình biên dịch tối ưu hóa mã của bạn.

Tất nhiên một số trình biên dịch không biết về __builtin_unreachable.

Và bạn cũng có thể xem xét kêu gọi một số [[noreturn]] hàm C++ (trong C++ 11 hoặc cao hơn) - hoặc __attribute__((noreturn)) cho GCC, chẳng hạn như abort()

BTW, assert(0) là không chính xác như ném một số ngoại lệ (vì trường hợp ngoại lệ có thể bị bắt)

+0

khẳng định tăng tín hiệu, IIRC, mà thực sự có thể bị bắt. – edmz

+0

@black: Gọi là 'hủy bỏ'. –

+0

Không, IIRC, việc thực hiện 'assert' (hoặc của' abort') đặt lại trình xử lý tín hiệu thành hành vi mặc định trước khi tăng nó –