<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 牛人業(yè)話(huà) > C語(yǔ)言的那些小秘密之斷言

C語(yǔ)言的那些小秘密之斷言

作者: 時(shí)間:2015-04-04 來(lái)源:網(wǎng)絡(luò ) 收藏

  每次寫(xiě)摘要我都覺(jué)得是一件很頭疼的事兒,因為我知道摘要真的很重要,它幾乎直接就決定了讀者的數量??赡芑司帕⒅?xiě)出來(lái)的東西,因為摘要的失敗而前功盡棄,因為絕大多數的讀者看文章之前都會(huì )瀏覽下摘要,如果他們發(fā)現摘要“不對口”,沒(méi)有什么特色和吸引人的地方,那么輕則采用一目十行的方法看完全文,重則對文章判“死刑”,一篇文章的好壞雖然不能用摘要來(lái)衡量,但是它卻常常被讀者用來(lái)衡量一篇文章的好壞,從而成為了文章讀者數量多少的一個(gè)關(guān)鍵因素。下面言歸正傳來(lái)說(shuō)說(shuō),如果出于一般性的學(xué)習,應付考試的話(huà),我想很少有人會(huì )在代碼中使用,可能有的人在此之前從來(lái)沒(méi)有使用過(guò)。那么斷言的使用到底能給我們的代碼帶來(lái)什么呢?我盡可能的把我所理解的斷言的使用講解清楚,希望我在此所講的斷言能夠對你有所幫助,讓你以后能夠在代碼中靈活使用斷言。

本文引用地址:http://dyxdggzs.com/article/272070.htm

  在講解之前,我們先來(lái)對斷言做一個(gè)基本的介紹,讓大家對斷言有一個(gè)大致的了解。在使用編寫(xiě)工程代碼時(shí),我們總會(huì )對某種假設條件進(jìn)行檢查,斷言就是用于在代碼中捕捉這些假設,可以將斷言看作是異常處理的一種高級形式。斷言表示為一些布爾表達式,程序員相信在程序中的某個(gè)特定點(diǎn)該表達式值為真??梢栽谌魏螘r(shí)候啟用和禁用斷言驗證,因此可以在測試時(shí)啟用斷言,而在部署時(shí)禁用斷言。同樣,程序投入運行后,最終用戶(hù)在遇到問(wèn)題時(shí)可以重新起用斷言。它可以快速發(fā)現并定位軟件問(wèn)題,同時(shí)對系統錯誤進(jìn)行自動(dòng)報警。斷言可以對在系統中隱藏很深,用其它手段極難發(fā)現的問(wèn)題可以用斷言來(lái)進(jìn)行定位,從而縮短軟件問(wèn)題定位時(shí)間,提高系統的可測性。實(shí)際應用時(shí),可根據具體情況靈活地設計斷言。

  通過(guò)上面的講解我們對于斷言算是有了一個(gè)大概的了解,那么接下來(lái)我們就來(lái)看看中assert宏在代碼中的使用。

  原型定義:

  void assert( int expression );

  assert宏的原型定義在中,其作用是先計算表達式 expression ,如果expression的值為假(即為0),那么它先向stderr打印一條出錯信息,然后通過(guò)調用abort 來(lái)終止程序運行。

  下面來(lái)看看一段代碼:

  #include

  #include

  int main( void )

  {

  int i;

  i=1;

  assert(i++);

  printf("%dn",i);

  return 0;

  }

  運行結果為:

  

 

  看看運行結果,因為我們給定的i初始值為1,所以使用assert(i++);語(yǔ)句的時(shí)候不會(huì )出現錯誤,進(jìn)而執行了i++,所以其后的打印語(yǔ)句輸出值為2。如果我們把i的初始值改為0,那么就回出現如下錯誤。

  Assertion failed: i++, file E:fdsaassert2.cpp, line 8

  Press any key to continue

  是不是發(fā)現根據提示很快就能定位出錯點(diǎn)呢?!既然assert這么便于定位出錯點(diǎn),看來(lái)的確我們有必要熟練的在代碼中使用它,但是什么東西的使用都是有規則的,assert的使用也不例外。

  斷言語(yǔ)句不是永遠會(huì )執行,可以屏蔽也可以啟用,這就要求assert不管是在屏蔽還是啟用的情況下都不能對我們本身代碼的功能有所影響,這樣的話(huà)剛才我們在代碼中使用了一句assert(i++);是不妥的,因為我們一旦禁用了assert,i++的語(yǔ)句就得不到執行,對于接下來(lái)i值的使用就會(huì )出現問(wèn)題了,所以對于這樣的語(yǔ)句我們應該是要分開(kāi)來(lái)實(shí)現,寫(xiě)出如下兩句來(lái)替代, assert(i); i++;,所以這就對于斷言的使用有了相應的要求,那么我們一般在什么情況下使用斷言呢?主要體現在一下幾個(gè)方面:

  1.可以在預計正常情況下程序不會(huì )到達的地方放置斷言。(如assert (0);)

  2.使用斷言測試方法執行的前置條件和后置條件 。

  3.使用斷言檢查類(lèi)的不變狀態(tài),確保任何情況下,某個(gè)變量的狀態(tài)必須滿(mǎn)足。(如某個(gè)變量的變化范圍)

  對于上面的前置條件和后置條件可能有的讀者還不是很了解,那么看看下面的解釋你就明白了。

  前置條件斷言:代碼執行之前必須具備的特性

  后置條件斷言:代碼執行之后必須具備的特性

  前后不變斷言:代碼執行前后不能變化的特性

  當然在使用的斷言的過(guò)程中會(huì )有一些我們應該注意的事項和養成一些良好的習慣,如:

  1.每個(gè)assert只檢驗一個(gè)條件,因為同時(shí)檢驗多個(gè)條件時(shí),如果斷言失敗,我們就無(wú)法直觀(guān)的判斷是哪個(gè)條件失敗

  2.不能使用改變環(huán)境的語(yǔ)句,就像我們上面的代碼改變了i變量,在實(shí)際編寫(xiě)代碼的過(guò)程中是不能這樣做的

  3.assert和后面的語(yǔ)句應空一行,以形成邏輯和視覺(jué)上的一致感,也算是一種良好的編程習慣吧,讓編寫(xiě)的代碼有一種視覺(jué)上的美感

  4.有的地方,assert不能代替條件過(guò)濾

  5.放在函數參數的入口處檢查傳入參數的合法性

  6.斷言語(yǔ)句不可以有任何邊界效應

  上面那么多的文字,似乎很枯燥,但是沒(méi)辦法,我們不能急功近利,還是要先堅持看完文字描述部分,這樣在下面我們分析代碼的過(guò)程中就能很快知道為什么會(huì )出現那樣的問(wèn)題了,也能在自己編寫(xiě)代碼的時(shí)候熟練的使用assert,給自己的代碼調試帶來(lái)極大的便利,尤其是你在用C語(yǔ)言做工程項目的時(shí)候,如果你能夠在你的代碼中合理的使用assert,能使你創(chuàng )建更穩定、質(zhì)量更好且不易于出錯的代碼。當需要在一個(gè)值為FALSE時(shí)中斷當前操作的話(huà),可以使用斷言。單元測試必須使用斷言,除了類(lèi)型檢查和單元測試外,斷言還提供了一種確定各種特性是否在程序中得到維護的極好的方法。但凡優(yōu)秀的程序員都能夠在自己代碼中很好的使用assert,編寫(xiě)出高質(zhì)量的代碼來(lái)。

  說(shuō)了assert這么多的有點(diǎn),當然也要說(shuō)說(shuō)它的缺點(diǎn)了。

  使用assert的缺點(diǎn)是,頻繁的調用會(huì )極大的影響程序的性能,增加額外的開(kāi)銷(xiāo)。所以在調試結束后,可以通過(guò)在包含#include 的語(yǔ)句之前插入 #define NDEBUG 來(lái)禁用assert調用。

  接下面分析一下下面的一段代碼:

  #include

  //#define NDEBUG

  #include

  int copy_string(char from[],char to[])

  {

  int i=0;

  while(to[i++]=from[i]);

  printf("%sn",to);

  return 1;

  }

  int main()

  {

  char str[]="this is a string!";

  char dec_str[206];

  printf("%sn",str);

  assert(copy_string(str,dec_str));

  printf("%sn",dec_str);

  return 0;

  }

  運行結果為:

  

 

  在以上代碼的開(kāi)頭部分我們把#define NDEBUG給注釋掉了,所以我們啟用了assert,main函數中使用了assert(copy_string(str,dec_str));來(lái)實(shí)現copy_string函數的調用,在copy_string函數中我們使用了一句return 1,所以最終的函數調用結果就等價(jià)于是assert(1),所以接下來(lái)繼續執行assert下面的打印語(yǔ)句,最終成功的打印了三條輸出語(yǔ)句,如果我們把開(kāi)頭的注釋部分打開(kāi),結果就只能成功的輸出起始部分一條打印語(yǔ)句。

  以上我們都是在圍繞著(zhù)assert宏在講解,僅僅是教會(huì )大家如何來(lái)使用assert宏,那么接下來(lái)看看我們如何來(lái)實(shí)現自己的斷言呢?

  接下來(lái)我們看看另外一段代碼:

  #include

  //#undef _EXAM_ASSERT_TEST_ //禁用

  #define _EXAM_ASSERT_TEST_ //啟用

  #ifdef _EXAM_ASSERT_TEST_ //啟用斷言測試

  void assert_report( const char * file_name, const char * function_name, unsigned int line_no )

  {

  printf( "n[EXAM]Error Report file_name: %s, function_name: %s, line %un",

  file_name, function_name, line_no );

  }

  #define ASSERT_REPORT( condition )

  do{

  if ( condition )

  NULL;

  else

  assert_report( __FILE__, __func__, __LINE__ );

  }while(0)

  #else // 禁用斷言測試

  #define ASSERT_REPORT( condition ) NULL

  #endif /* end of ASSERT */

  int main( void )

  {

  int i;

  i=0;

  // assert(i++);

  ASSERT_REPORT(i);

  printf("%dn",i);

  return 0;

  }

  運行結果如下:

  [EXAM]Error Report file_name: assert3.c, function_name: main, line 29

  0

  細心的讀者會(huì )發(fā)現我們并沒(méi)有使用斷言來(lái)結束當前程序的執行,所以在斷言下面的printf成功的打印出了i的當前值,當然我們也可以做適當的修改,在斷言出發(fā)現錯誤,那么就調用 abort();來(lái)使當前正在執行的程序異常終止,修改如下:

  #include

  #include

  //#undef _EXAM_ASSERT_TEST_ //禁用

  #define _EXAM_ASSERT_TEST_ //啟用

  #ifdef _EXAM_ASSERT_TEST_ //啟用斷言測試

  void assert_report( const char * file_name, const char * function_name, unsigned int line_no )

  {

  printf( "n[EXAM]Error Report file_name: %s, function_name: %s, line %un",

  file_name, function_name, line_no );

  abort();

  }

  #define ASSERT_REPORT( condition )

  do{

  if ( condition )

  NULL;

  else

  assert_report( __FILE__, __func__, __LINE__ );

  }while(0)

  #else // 禁用斷言測試

  #define ASSERT_REPORT( condition ) NULL

  #endif /* end of ASSERT */

  int main( void )

  {

  int i;

  i=0;

  // assert(i++);

  ASSERT_REPORT(i);

  printf("%dn",i);

  return 0;

  }

  運行結果如下:

  [EXAM]Error Report file_name: assert3.c, function_name: main, line 31

  Aborted

  此時(shí)就不會(huì )在執行接下來(lái)的打印語(yǔ)句了??纯次覀冏约旱膶?shí)現方式就知道,我們自己編寫(xiě)的斷言可以比直接調用assert宏可以得到更多的信息量,主要是由于我們自己編寫(xiě)的斷言更加的具有靈活性,可以根據自己的需要來(lái)打印輸出不同的信息,同時(shí)也可以對于不同類(lèi)型的錯誤或者警告信息使用不同的斷言,這也是在工程代碼中經(jīng)常使用的做法。如果你在關(guān)注代碼運行結果的同時(shí)也認真的閱讀了我的代碼,你會(huì )發(fā)現其中我在宏定義中使用了一個(gè)do{}while(0),使用它有什么好處呢,或許在以上的代碼中并沒(méi)有體現出來(lái),那么我們看看下面的代碼你就知道了。

  #include

  void print_1(void)

  {

  printf("print_1n");

  }

  void print_2(void)

  {

  printf("print_2n");

  }

  #define printf_value()

  print_1();

  print_2();

  int main( void )

  {

  int i=0;

  if(i==1)

  printf_value();

  return 0;

  }

  運行結果:

  

 

  還是備份一下文章描述,以防圖片打開(kāi)失敗給讀者帶來(lái)困擾。

  print_2

  Press any key to continue

  看了上面運行結果可能有的讀者會(huì )很疑惑為什么會(huì )出現以上的錯誤呢?!if語(yǔ)句的條件不滿(mǎn)足,那么print_value()函數應該不會(huì )被調用啊,怎么會(huì )打印呢。如果我們把上面的printf_value()替換為 print_1(); print_2();,就會(huì )很清楚的發(fā)現if語(yǔ)句在此的作用僅僅是不調用print_1();,而print_2();在控制之外,所以出現了上面的結果,有的讀者可能會(huì )馬上想到我們加上一個(gè){}不就好了嗎,在這里的確是加一個(gè){}就可以了,因為這里是一個(gè)特殊情況,沒(méi)有else語(yǔ)句,如果我們在以上的宏定義中使用{},加入else語(yǔ)句后再來(lái)看看代碼。

  #include

  void print_1(void)

  {

  printf("print_1n");

  }

  void print_2(void)

  {

  printf("print_2n");

  }

  #define printf_value()

  {

  print_1();

  print_2();}

  int main( void )

  {

  int i=0;

  if(i==1)

  printf_value();

  else

  printf("add else word!!!");

  return 0;

  }

  看似正確的代碼,我們編譯就會(huì )出現如下錯誤:

  error C2181: illegal else without matching if

  為什么會(huì )出現這樣的錯誤呢?因為我們編寫(xiě)C語(yǔ)言代碼時(shí),在每個(gè)語(yǔ)句后面加分號是一種約定俗成的習慣,以上代碼中我們在printf_value()語(yǔ)句后面加了一個(gè)分號,正是由于這個(gè)分號的作用使得else沒(méi)有與之相對應的if,所以編譯出錯。但是如果我們使用do{}while(0)就不會(huì )出現這些問(wèn)題,所以我們在編寫(xiě)代碼的時(shí)候應該學(xué)會(huì )在宏定義中使用do{}while(0)。

  C語(yǔ)言斷言?xún)热莸闹v解到此就該結束了,上面內容已給出了在C語(yǔ)言編寫(xiě)代碼的過(guò)程中斷言較為詳細的使用,其中后面使用我們自己實(shí)現的斷言算得上是一個(gè)比較經(jīng)典的斷言設計方法了,讀者可以在自己以后編寫(xiě)C語(yǔ)言代碼的過(guò)程中參考下。由于本人水平有限,博客中的不妥或錯誤之處在所難免,殷切希望讀者批評指正。同時(shí)也歡迎讀者共同探討相關(guān)的內容,如果樂(lè )意交流的話(huà)請留下你寶貴的意見(jiàn)。

c語(yǔ)言相關(guān)文章:c語(yǔ)言教程




關(guān)鍵詞: C語(yǔ)言 斷言

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>