深刻理解斷言assert()函數(待整理)



assert()函數用法總結

  assert宏的原型定義在<assert.h>中,其做用是若是它的條件返回錯誤,則終止程序執行,原型定義:html

#include <assert.h> void assert( int expression );

  assert的做用是現計算表達式 expression ,若是其值爲假(即爲0),那麼它先向stderr打印一條出錯信息,而後經過調用 abort 來終止程序運行。請看下面的程序清單badptr.c:express

複製代碼
#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.函數

  已放棄使用assert()的缺點是,頻繁的調用會極大的影響程序的性能,增長額外的開銷。在調試結束後,能夠經過在包含#include <assert.h>的語句以前插入 #define NDEBUG 來禁用assert調用,示例代碼以下:工具

#include <stdio.h> #define NDEBUG #include <assert.h>


用法總結與注意事項:性能

  1)在函數開始處檢驗傳入參數的合法性如:spa

複製代碼
int resetBufferSize(int nNewSize) {   //功能:改變緩衝區大小,   //參數:nNewSize 緩衝區新長度   //返回值:緩衝區當前長度   //說明:保持原信息內容不變 nNewSize<=0表示清除緩衝區   assert(nNewSize >= 0);   assert(nNewSize <= MAX_BUFFER_SIZE);   ... }
複製代碼

  

  2)每一個assert只檢驗一個條件,由於同時檢驗多個條件時,若是斷言失敗,沒法直觀的判斷是哪一個條件失敗,如:.net

  很差:調試

assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);

  好:code

assert(nOffset >= 0); assert(nOffset+nSize <= m_nInfomationSize);

  3)不能使用改變環境的語句,由於assert只在DEBUG個生效,若是這麼作,會使用程序在真正運行時遇到問題,如:htm

  錯誤:

assert(i++ < 100);

  這是由於若是出錯,好比在執行以前i=100,那麼這條語句就不會執行,那麼i++這條命令就沒有執行。

  正確:

 assert(i < 100); i++;

  4)assert和後面的語句應空一行,以造成邏輯和視覺上的一致感。

  5)有的地方,assert不能代替條件過濾。
assert是用來避免顯而易見的錯誤的,而不是處理異常的。錯誤和異常是不同的,錯誤是不該該出現的,異常是不可避免的。c語言異常能夠經過條件判斷來處理,其它語言有各自的異常處理機制。

一個很是簡單的使用assert的規律就是,在方法或者函數的最開始使用,若是在方法的中間使用則須要慎重考慮是不是應該的。方法的最開始還沒開始一個功能過程,在一個功能過程執行中出現的問題幾乎都是異常。

*****************************************************************************************************************************

#define NDEBUG是what?

Re:#define NDEBUG是what? 按照字母序首先咱們來看看<assert.h>,這個文件提供的接口功能很簡單,但倒是咱們極其經常使用的功能-斷言機制(若是條件爲False,則輸出Diagnostics信息,而後Abort)。固然在最終產品中使用斷言並非一種好的方法,不過斷言是一種頗有用的幫助咱們調試程序的好工具。 咱們通常在程序的調試版本中使用斷言機制,通常用來對Input進行Validation,輸出一些Diagnostics信息。如: assert((idx > 10) && (idx < 100)); < assert.h>中提供一個宏assert,這個宏的功能由另外一個宏NDEBUG(標誌是不是DEBUG版本)決定。若是NDEBUG宏在你include <assert.h>時沒有被定義,這時斷言功能開啓;不然斷言功能關閉。如: #define NDEBUG #include <assert.h> /* 此時斷言功能關閉 */ 你也大可沒必要在你的各個源文件中控制斷言功能的開關,在編譯器選項中一樣能夠定義NDEBUG宏,如gcc -DNDEBUG test.c,固然對於大的project,這些是應該放在Makefile中的,這樣的結果就至關於在你全部#include <assert.h>的地方以前定義了NDEBUG宏,也就是說在每一個編譯單元中,斷言功能都是關閉的。 assert宏看起來很簡單,可是因爲其是C標準庫提供的接口,因此在實現的時候須要考慮的更加細緻和全面一些。從上面的敘述上來看assert.h文件的結構應該大體以下: #undef assert #ifdef NDEBUG #define assert(test) ((void)0) #else #define assert(test) ... #endif 咱們能夠很輕鬆的就拿出一個assert的實現版本: /* NDEBUG not defined */ #define assert(test) if (!(test)) \ fprintf(stderr, "Assertion Failed: %s, file %s, line %d\n", \ #test, __FILE__, __LINE__); \ 那麼這個版本的實現能夠接受不,答案是不能。緣由有如下幾點: 1) 這個實現中直接用到了stderr和fprintf,這兩個符號都是在<stdio.h>中聲明的,可是C標準庫頭文件基本上都是各自獨立的,在<assert.h>中是不會再包含其餘頭文件的,那麼這就要求使用assert的程序本身包含<stdio.h>,這顯然不符合一個C標準庫的基本要求; 2) assert宏應該最終展開爲一個void expression,由於用戶極可能在他們的程序中寫出像(assert(0 < x), x < y)這樣的代碼來,而在上面的實現版本中,顯然assert展開後不是一個void expression。 咱們再來看看P.J.Plauger的實現版本: /* NDEBUG not defined */ void _Assert(char *); #define _STR(x) _VAL(x) #define _VAL(x) #x #define assert(test) (test) ? (void)0 \ : _Assert(__FILE__ ":" _STR(__LINE__) " " #test) /* in xassert.c */ #include <assert.h> #include <stdio.h> void _Assert(char *msg) { fprintf(stderr, "%s -- assertion failed\n", msg); abort(); }    分析一下這一版本的實現,首先assert宏並無直接調用任何庫輸出函數,而是調用了一個本身實現的函數_Assert,把向stderr輸出診斷信息的活都交給了_Assert。_STR和_VAL是兩個輔助宏,用來將__LINE__字符串化。這裏比較難懂的地方就是_Assert(__FILE__ ":" _STR(__LINE__) " " #test)這一句,其實這個也很好理解。看看下面語句的執行結果: printf("%s\n", "Hello" " " "Tony!"); 執行上面語句你會看到Hello Tony!,這樣一來實際上_Assert(__FILE__ ":" _STR(__LINE__) " " #test)就能夠被理解爲: _Assert("THE_FILENAME_STRING" ":" "THE_LINE_STRING" " " "THE_TEST_STRING")

相關文章
相關標籤/搜索