基本解釋
及物動詞 vt.
1.斷言,聲稱[+that][O2]
She asserted her innocence.
她宣稱她是清白的。
He asserted that he was not guilty.
他聲明他無罪。
2.維護,堅持;主張擁有
The boss asserted his authority by punishing his employees.
老闆靠懲罰雇員來維護自己的權威。
3.顯示;确立;聲明,維護,宣稱
C裡用法
使用斷言可以創建更穩定,品質更好且不易于出錯的代碼。當需要在一個值為FALSE時中斷當前操作的話,可以使用斷言。單元測試必須使用斷言(Junit/JunitX)。
除了類型檢查和單元測試外,斷言還提供了一種确定各種特性是否在程序中得到維護的極好的方法。
使用斷言使我們向按契約式設計更近了一步。
斷言特性
前置條件斷言:代碼執行之前必須具備的特性。
後置條件斷言:代碼執行之後必須具備的特性。
前後不變斷言:代碼執行前後不能變化的特性。
java斷言
斷言在默認情況下是關閉的,要在編譯時啟用斷言,需要使用source1.4标記 既javac source1.4 Test.java ,在運行時啟用斷言需要使用 -ea參數 。要在系統類中啟用和禁用斷言可以使用 -esa和 -dsa參數。
例如:
publicclassAssertExampleOne{
publicAssertExampleOne(){}
publicstaticvoidmain(Stringargs[]){
intx=10;
System.out.println("TestingAssertionthatx==100");
assertx==100:"Outassertionfailed!";
System.out.println("Testpassed!");
}
}
如果編譯時未加 -source1.4,則編譯通不過
在執行時未加 -ea 時輸出為
Testing Assertion that x==100
Test passed
jre忽略了斷言的舊代碼,而使用了該參數就會輸出為
Testing Assertion that x==100
Exception in thread "main" java.lang.AssertionError: Out assertion failed!
at AssertExampleOne.main(AssertExampleOne.java:6)
斷言的副作用
由于程序員的問題,斷言的使用可能會帶來副作用 ,例如:
boolean isEnable=false;
//...
assert isEnable=true;
這個斷言的副作用是因為它修改了程序中變量的值并且未抛出錯誤,這樣的錯誤如果不細心的檢查是很難發現的。但是同時我們可以根據以上的副作用得到一個有用的特性,根據它來測試斷言是否打開。
publicclassAssertExampleTwo{
publicstaticvoidmain(Stringargs[]){
booleanisEnable=false;
//...
assertisEnable=true;
if(isEnable==false){
thrownewRuntimeException("Assertionshoulebeenable!");
}
}
}
使用斷言
1.可以在預計正常情況下程序不會到達的地方放置斷言 :assert false。
2.斷言可以用于檢查傳遞給私有方法的參數。(對于公有方法,因為是提供給外部的接口,所以必須在方法中有相應的參數檢驗才能保證代碼的健壯性)。
3.使用斷言測試方法執行的前置條件和後置條件。
4.使用斷言檢查類的不變狀态,确保任何情況下,某個變量的狀态必須滿足。(如age屬性應大于0小于某個合适值)。
不用斷言
斷言語句不是永遠會執行,可以屏蔽也可以啟用
因此:
1.不要使用斷言作為公共方法的參數檢查,公共方法的參數永遠都要執行。
2.斷言語句不可以有任何邊界效應,不要使用斷言語句去修改變量和改變方法的返回值。
C裡的宏
宏名: assert
功 能: 測試一個條件并可能使程序終止
用 法: void assert(int test);
程序例:
#include
#include
#include
structITEM
{
intkey;
intvalue;
};
/*additemtolist,makesurelistisnotnull*/
voidadditem(structITEM*itemptr)
{
assert(itemptr!=NULL);
/*additemtolist*/
}
intmain(void)
{
additem(NULL);
return0;
}
assert() 宏用法
注意:assert是宏,而不是函數。在C的assert.h頭文件中。
assert宏的原型定義在
#defineassert(expr)
((expr)
?__ASSERT_VOID_CAST(0)
:__assert_fail(__STRING(expr),__FILE__,__LINE__,__ASSERT_FUNCTION))
/*DefinedInGlibc2.15*/
assert的作用是先計算表達式expr,如果其值為假(即為0),那麼它會打印出來assert的内容和__FILE__, __LINE__, __ASSERT_FUNCTION,然後執行abort()函數使kernel殺掉自己并coredump(是否生成coredump文件,取決于系統配置);否則,assert()無任何作用。宏assert()一般用于确認程序的正常操作,其中表達式構造無錯時才為真值。完成調試後,不必從源代碼中删除assert()語句,因為宏NDEBUG有定義時,宏assert()的定義為空。
請看下面的程序清單badptr.c:
#include
#include
#include
intmain(void){
FILE*fp;
fp=fopen("test.txt","w");//以可寫的方式打開一個文件,如果不存在就創建一個同名文件
assert(fp);//所以這裡不會出錯
fclose(fp);
fp=fopen("noexitfile.txt","r");//以隻讀的方式打開一個文件,如果不存在就打開文件失敗
assert(fp);//所以這裡出錯
fclose(fp);//程序永遠都執行不到這裡來
return0;
}
[root@localhost error_process]# gcc badptr.c
[root@localhost error_process]# ./a.out
a.out: badptr.c:14: main: Assertion `fp' failed.
如果使用動态鍊接libc,那麼除了__FILE__, __LINE__, __ASSERT_FUNCTION會讓目标變的稍稍大了一點,并不會因為多次使用assert()增加目标很多。不過好處也很明顯,就是會在assert的地方會打印出來文件名,行數,和函數名。另外,要注意用assert()的錯誤程度。如果assert()的條件fail了,那麼會調用abort()函數讓kernel殺掉自己,哪怕用戶自己重新注冊了SIGABRT信号的行為(abort()會先向自己發送信号SIGABRT保證用戶的handler正确執行,然後修改SIGABRT信号的行為為默認行為coredump,再次像自己發送SIGABRT,coredump)。
在調試結束後,可以通過在包含#include
#include
用法總結與注意事項:
1)在函數開始處檢驗傳入參數的合法性
如:
int resetBufferSize(int nNewSize){ //功能:改變緩沖區大小, //參數:nNewSize緩沖區新長度 //返回值:緩沖區當前長度 //說明:保持原信息内容不變 nNewSize<=0表示清除緩沖區 assert(nNewSize >= 0); assert(nNewSize <= MAX_BUFFER_SIZE); ...}
2)每個assert隻檢驗一個條件,因為同時檢驗多個條件時,如果斷言失敗,無法直觀的判斷是哪個條件失敗
/***不好***/assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);/****好****/assert(nOffset >= 0);assert(nOffset+nSize <= m_nInfomationSize);
3)不能使用改變環境的語句,因為assert隻在DEBUG生效,如果這麼做,會使用程序在真正運行時遇到問題
錯誤: assert(i++ < 100)
這是因為如果出錯,比如在執行之前i=100,那麼這條語句就不會執行,那麼i++這條命令就沒有執行。
正确: assert(i < 100)
i++;
4)assert和後面的語句應空一行,以形成邏輯和視覺上的一緻感
5)有的地方,assert不能代替條件過濾
注意:當對于浮點數:
#include
在switch語句中總是要有default子句來顯示信息(Assert)。
int number = SomeMethod();switch(number){ case 1: Trace.WriteLine("Case 1:"); break; case 2: Trace.WriteLine("Case 2:"); break; default : Debug.Assert(false); break;}