天天看點

Assert_KeyWord

斷言(軟體開發)

維基百科,自由的百科全書

跳轉到導航跳轉到搜尋

本文是關于計算機程式設計概念的。對于安全性斷言标記語言(SAML)開放标準的上下文中的斷言,請參閱安全性斷言标記語言§斷言。

在計算機程式設計,使用特别是當指令式程式設計範式,一個斷言是謂詞(一個布爾值函數在狀态空間,通常表示為邏輯命題使用變量連接配接到在程式的點的程式的),即始終應該在代碼執行中評估為true。斷言可以幫助程式員讀取代碼,幫助編譯器編譯代碼,或幫助程式檢測自己的缺陷。

對于後者,一些程式通過在謂詞運作時實際評估謂詞來檢查斷言。然後,如果它實際上不是真的 - 斷言失敗 - ,程式認為自己被破壞并且通常故意崩潰或抛出斷言失敗異常。

##内容

1 詳情

2 用法

2.1 合同設計中的斷言

2.2 運作時檢查的斷言

2.3 開發周期中的斷言

2.4 生産環境中的斷言

2.5 靜态斷言

3 禁用斷言

4 與錯誤處理的比較

5 曆史

6 另見

7 參考文獻

8 外部連結

詳情

以下代碼包含兩個斷言,x > 0并且x > 1在執行期間它們确實在指定的點處為真:

x = 1 ;

斷言 x > 0 ;

x ++ ;

斷言 x > 1 ;

程式員可以使用斷言來幫助指定程式并推理程式的正确性。例如,放置在代碼段開頭的前提條件 - 斷言 - 确定程式員期望代碼執行的狀态集。甲後置在-放置最終描述了在執行結束時的預期狀态。例如:x > 0 { x++ } x > 1。

上面的示例使用符号來包含CAR Hoare在1969年的文章中使用的斷言。[1]該符号不能用于現有的主流程式設計語言。但是,程式員可以使用其程式設計語言的注釋功能包含未經檢查的斷言。例如,在C中:

x = 5 ;

x = x + 1 ;

// {x> 1}

注釋中包含的大括号有助于将注釋的使用與其他用途區分開來。

庫也可以提供斷言功能。例如,在C中使用glibc并支援C99:

#include <assert.h>

int f (void )

{

int x = 5 ;

x = x + 1 ;

斷言(x > 1 );

}

一些現代程式設計語言包括已檢查的斷言 - 在運作時或有時靜态檢查的語句。如果斷言在運作時評估為false,則會導緻斷言失敗,這通常會導緻執行中止。這引起了對檢測到邏輯不一緻的位置的關注,并且可能優先于否則将導緻的行為。

斷言的使用有助于程式員設計,開發和推理程式。

用法

在諸如Eiffel之類的語言中,斷言構成了設計過程的一部分; 其他語言(如C和Java)僅用于檢查運作時的假設。在這兩種情況下,可以在運作時檢查它們的有效性,但通常也可以抑制它們。

合同設計中的斷言

斷言可以作為一種形式的文檔:它們可以描述代碼在運作之前期望找到的狀态(它的前置條件),以及代碼在運作完成後期望産生的狀态(後置條件); 他們還可以指定不變量 a的類。Eiffel将這些斷言內建到語言中并自動提取它們以記錄該類。這構成了合同設計方法的重要組成部分。

這種方法在沒有明确支援它的語言中也很有用:在注釋中使用斷言語句而不是斷言的優點是程式可以在每次運作時檢查斷言; 如果斷言不再成立,則可以報告錯誤。這可以防止代碼與斷言失去同步。

運作時檢查的斷言

斷言可以用于驗證程式實作期間程式員在程式執行時保持有效的假設。例如,請考慮以下Java代碼:

int total = countNumberOfUsers ();

if (total % 2 == 0 ) {

// total is even

} else {

// total is odd and non-negative

assert total % 2 == 1 ;

}

在Java中,%是餘數運算符(modulo),在Java中,如果它的第一個操作數是負數,結果也可以是負數(與數學中使用的模數不同)。程式員假設這total是非負的,是以2的除法的餘數将始終為0或1.斷言使此假設顯式:如果countNumberOfUsers确實傳回負值,則程式可能有錯誤。

這種技術的一個主要優點是,當确實發生錯誤時,會立即直接檢測到錯誤,而不是通過其常常模糊的副作用。由于斷言失敗通常會報告代碼位置,是以通常可以精确定位錯誤而無需進一步調試。

斷言有時也放在執行不應達到的點上。例如,斷言可以放在語句的default子句中,switch例如C,C ++和Java。程式員故意不處理的任何情況都會引發錯誤,程式将中止而不是默默地繼續處于錯誤狀态。在D中,當switch語句不包含default子句時,會自動添加斷言。

在Java中,自版本1.4以來,斷言一直是語言的一部分。斷言失敗導緻AssertionError程式在使用适當的标志運作時引發,如果沒有該标志,則忽略斷言語句。在C中,它們被assert.h定義為宏的标準頭添加,該宏在發生故障時發出錯誤信号,通常終止程式。在C ++中,both 和headers都提供了宏。 assert (assertion) assert.hcassertassert

斷言的危險在于它們可能通過更改記憶體資料或更改線程時序來引起副作用。應謹慎實施斷言,以免對程式代碼産生任何副作用。

語言中的斷言構造允許在不使用第三方庫的情況下輕松進行測試驅動開發(TDD)。

開發周期中的斷言

在開發周期中,程式員通常會在啟用斷言的情況下運作程式。當發生斷言失敗時,程式員會立即收到問題通知。許多斷言實作也将停止程式的執行:這很有用,因為如果程式在發生斷言違規後繼續運作,它可能會破壞其狀态并使問題的原因更難以找到。使用斷言失敗提供的資訊(例如失敗的位置和堆棧跟蹤,如果環境支援核心轉儲或者程式在調試器中運作,甚至是完整的程式狀态),程式員通常可以解決問題。是以斷言提供了一個非常強大的調試工具。

生産環境中的斷言

當程式部署到生産中時,通常會關閉斷言,以避免它們可能産生的任何開銷或副作用。在某些情況下,部署的代碼中完全沒有斷言,例如通過宏的C / C ++斷言。在其他情況下,例如Java,斷言存在于已部署的代碼中,并且可以在字段中打開以進行調試。[2]

斷言還可以用于向編譯器承諾給定邊緣條件實際上不可達,進而允許某些本來不可能的優化。在這種情況下,禁用斷言實際上可能會降低性能。

靜态斷言

在編譯時檢查的斷言稱為靜态斷言。

靜态斷言在編譯時模闆元程式設計中特别有用,但如果(并且僅當)斷言失敗,也可以通過引入非法代碼在像C這樣的低級語言中使用。C11和C ++ 11直接支援靜态斷言static_assert。在早期的C版本中,可以實作靜态斷言,例如,如下所示:

#define SASSERT(pred)switch(0){case 0:case pred:;}

SASSERT ( BOOLEAN CONDITION );

如果(BOOLEAN CONDITION)部件的計算結果為false,則上述代碼将無法編譯,因為編譯器不允許兩個具有相同常量的大小寫标簽。布爾表達式必須是編譯時常量值,例如,(sizeof(int)==4)它将是該上下文中的有效表達式。此構造在檔案範圍内不起作用(即不在函數内),是以必須将其包裝在函數内。

在C中實作斷言的另一種流行的[3]方法是:

static char const static_assertion [ (BOOLEAN CONDITION )

? 1 : - 1

] = { ‘!’ };

如果(BOOLEAN CONDITION)部件的計算結果為false,則上述代碼将無法編譯,因為數組可能沒有負長度。如果事實上編譯器允許負長度,則初始化位元組(’!'部分)應該導緻即使是這樣過于寬松的編譯器也會抱怨。布爾表達式必須是編譯時常量值,例如,(sizeof(int)==4)它将是該上下文中的有效表達式。

這兩種方法都需要一種構造唯一名稱的方法。現代編譯器支援__COUNTER__預處理器定義,通過為每個編譯單元傳回單調遞增的數字,便于構造唯一名稱。[4]

D通過使用提供靜态斷言static assert。[5]

禁用斷言

大多數語言允許在全局啟用或禁用斷言,有時獨立啟用或禁用斷言。斷言通常在開發期間啟用,在最終測試期間和向客戶釋出時禁用。不檢查斷言避免了評估斷言的成本,同時(假設斷言沒有副作用)仍然在正常條件下産生相同的結果。在異常情況下,禁用斷言檢查可能意味着已中止的程式将繼續運作。這有時是優選的。

某些語言(包括C和C ++)使用預處理器在編譯時完全删除斷言。Java需要将一個選項傳遞給運作時引擎才能啟用斷言。如果沒有該選項,則會繞過斷言,但它們始終保留在代碼中,除非在運作時由JIT編譯器優化或在編譯時由if(false)條件排除,是以它們不需要具有運作時空間或Java中的時間成本。

程式員可以通過繞過或操縱語言的正常斷言檢查機制來建構對其代碼始終有效的檢查。

與錯誤處理的比較

斷言與正常錯誤處理不同。斷言記錄邏輯上不可能的情況并發現程式設計錯誤:如果不可能發生,那麼程式的根本就是明顯錯誤。這與錯誤處理不同:大多數錯誤條件都是可能的,盡管在實踐中可能不太可能發生某些錯誤條件。使用斷言作為通用錯誤處理機制是不明智的:斷言不允許從錯誤中恢複; 斷言失敗通常會突然停止程式的執行; 并且通常在生産代碼中禁用斷言。斷言也不會顯示使用者友好的錯誤消息。

請考慮以下使用斷言來處理錯誤的示例:

int * ptr = malloc (sizeof (int ) * 10 );

斷言(ptr );

//使用ptr

在這裡,程式員知道如果沒有配置設定記憶體,malloc它将傳回一個NULL指針。這是可能的:作業系統不保證每次調用malloc都會成功。如果發生記憶體不足錯誤,程式将立即中止。沒有斷言,程式将繼續運作直到ptr 被取消引用,可能更長,具體取決于所使用的特定硬體。隻要斷言沒有被禁用,就可以立即退出。但是如果需要優雅的失敗,程式必須處理失敗。例如,伺服器可能具有多個用戶端,或者可能包含不會幹淨地釋放的資源,或者可能具有未送出的更改以寫入資料存儲。在這種情況下,單個交易失敗比突然中止更好。

另一個錯誤是依賴于用作斷言參數的表達式的副作用。應該始終牢記斷言可能根本不會被執行,因為它們的唯一目的是驗證一個應該總是為真的條件實際上是否成立。是以,如果程式被認為是無錯誤并且被釋放,則可以禁用斷言并且将不再對其進行評估。

考慮前一個示例的另一個版本: