複製鏈接
請複製以下鏈接發送給好友

assert

(編程術語)

鎖定
編寫代碼時,我們總是會做出一些假設,斷言就是用於在代碼中捕捉這些假設,可以將斷言看作是異常處理的一種高級形式。斷言表示為一些布爾表達式,程序員相信在程序中的某個特定點該表達式值為真。可以在任何時候啓用和禁用斷言驗證,因此可以在測試時啓用斷言,而在部署時禁用斷言。同樣,程序投入運行後,最終用户在遇到問題時可以重新啓用斷言。
中文名
斷言
外文名
assert
前置條件斷言
代碼執行之前必須具備的特性
解    釋
維護,堅持;主張擁有等
注    意
assert是宏,而不是函數
類    型
布爾類型

assert術語簡介

使用斷言可以創建更穩定,品質更好且不易於出錯的代碼。當需要在一個值為FALSE時中斷當前操作的話,可以使用斷言。單元測試必須使用斷言(Junit/JunitX)。
除了類型檢查和單元測試外,斷言還提供了一種確定各種特性是否在程序中得到維護的極好的方法。
使用斷言使我們向按契約式設計更近了一步。

assert斷言特性

前置條件斷言:代碼執行之前必須具備的特性
後置條件斷言:代碼執行之後必須具備的特性
前後不變斷言:代碼執行前後不能變化的特性

assert使用方式

斷言可以有兩種形式
1.assert Expression1
2.assert Expression1:Expression2
其中Expression1應該總是一個布爾值,Expression2是斷言失敗時輸出的失敗消息的字符串。如果Expression1為假,則拋出一個 AssertionError,這是一個錯誤,而不是一個異常,也就是説是一個不可控制異常(unchecked Exception)

assertjava斷言

斷言在默認情況下是關閉的,要在編譯時啓用斷言,需要使用source1.4標記 即javac source1.4 Test.java ,在運行時啓用斷言需要使用 -ea參數 。要在系統類中啓用和禁用斷言可以使用 -ea和 -dsa參數。
例如:
public class AssertExampleOne{
    public AssertExampleOne(){}
    public static void main(String args[]){
        int x=10;
        System.out.println("Testing Assertion that x==100");
        assert x==100:"Out assertion failed!";
        System.out.println("Test passed!");
    }
}
如果編譯時未加 -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;
這個斷言的副作用是因為它修改了程序中變量的值並且未拋出錯誤,這樣的錯誤如果不細心的檢查是很難發現的。但是同時我們可以根據以上的副作用得到一個有用的特性,根據它來測試斷言是否打開。
public class AssertExampleTwo{
    public static void main(String args[]){
        boolean isEnable=false;
        assert isEnable=true;
        if(isEnable==false){
            throw newRuntimeException("Assertion should be enable!");
        }
    }
}

assert斷言使用方式

1.可以在預計正常情況下程序不會到達的地方放置斷言 :assert false
2.斷言可以用於檢查傳遞給私有方法的參數。(對於公有方法,因為是提供給外部的接口,所以必須在方法中有相應的參數檢驗才能保證代碼的健壯性
3.使用斷言測試方法執行的前置條件和後置條件
4.使用斷言檢查類的不變狀態,確保任何情況下,某個變量的狀態必須滿足。(如age屬性應大於0小於某個合適值)
不用斷言
斷言語句不是永遠會執行,可以屏蔽也可以啓用
因此:
1.不要使用斷言作為公共方法的參數檢查,公共方法的參數永遠都要執行
2.斷言語句不可以有任何邊界效應,不要使用斷言語句去修改變量和改變方法的返回值.
C裏的宏
宏名: assert
功 能: 測試一個條件並可能使程序終止
用 法: void assert(int test);
程序例:
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
struct ITEM
{
    int key;
    int value;
};
/*add item to list,make sure list is not null*/
void additem(struct ITEM* itemptr)
{
    assert(itemptr!=NULL);
    /*additemtolist*/
}
int main(void)
{
    additem(NULL);
    return 0;
}
assert() 宏用法
注意:assert是宏,而不是函數。在C的assert.h頭文件中。
assert宏的原型定義在<assert.h>中,其作用是如果它的條件返回錯誤,則終止程序執行,原型定義:
#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()的定義為空。 [1] 
請看下面的程序清單badptr.c:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
int main(void){
    FILE* fp;
    fp=fopen("test.txt","w");//以可寫的方式打開一個文件,如果不存在就創建一個同名文件
    assert(fp);//所以這裏不會出錯
    fclose(fp);
    fp=fopen("noexitfile.txt","r");//以只讀的方式打開一個文件,如果不存在就打開文件失敗
    assert(fp);//所以這裏出錯
    fclose(fp);//程序永遠都執行不到這裏來
    return 0;
}
[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 <assert.h>的語句之前插入 #define NDEBUG 來禁用assert調用,示例代碼如下:
#include <stdio.h>
#define NDEBUG
#include <assert.h>
用法總結與注意事項:
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<assert.h>
float pi=3.14f;
assert (pi==3.14f);
//在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;
}
參考資料
  • 1.    (美)赫伯特·希爾特.C語言大全(第四版) The Complete Reference C .北京:電子工業出版社,2001:300