夏天/isummer

Sun of my life !Talk is cheap, Show me the code! 追风赶月莫停留,平芜尽处是春山~

博客园 首页 新随笔 联系 管理

ASSERT断言

  编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新起用断言。

  代码执行之前必须具备的特性;

  assert是宏,而不是函数;
  使用断言可以创建更稳定,品质更好且不易于出错的代码。
  断言语句不是永远会执行,可以屏蔽也可以启用。
  
1. 断言使用条件
  (1)使用断言测试方法执行的前置条件和后置条件;
  (2)可以在预计正常情况下程序不会到达的地方放置断言 :assert false
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()的定义为空。

#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;
}

  在调试结束后,可以通过在包含#include <assert.h>的语句之前插入

  #define NDEBUG //来禁用assert调用

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

3. 用法总结与注意事项

  (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;
}

 4. C++中的用法

   void assert(  int expression ); 

  参数:Expression (including pointers) that evaluates to nonzero or 0.(表达式【包括指针】是非零或零)
  原理:assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行

 1 // crt_assert.c  
 2 // compile with: /c  
 3 #include <stdio.h>  
 4 #include <assert.h>  
 5 #include <string.h>  
 6   
 7 void analyze_string( char *string );   // Prototype  
 8   
 9 int main( void )  
10 {  
11    char  test1[] = "abc", *test2 = NULL, test3[] = "";  
12   
13    printf ( "Analyzing string '%s'\n", test1 ); fflush( stdout );  
14    analyze_string( test1 );  
15    printf ( "Analyzing string '%s'\n", test2 ); fflush( stdout );  
16    analyze_string( test2 );  
17    printf ( "Analyzing string '%s'\n", test3 ); fflush( stdout );  
18    analyze_string( test3 );  
19 }  
20   
21 // Tests a string to see if it is NULL,   
22 // empty, or longer than 0 characters.  
23 void analyze_string( char * string )  
24 {  
25    assert( string != NULL );        // Cannot be NULL  
26    assert( *string != '\0' );       // Cannot be empty  
27    assert( strlen( string ) > 2 );  // Length must exceed 2  
28 } 

  输出:

    Analyzing string 'abc'  
    Analyzing string '(null)'  
    Assertion failed: string != NULL, file assert.cpp, line 25  
abnormal program termination

  加入#define NDEBUG之后,上文第一个例子输出结果为:

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

  添加 #define NDEBUG后输出如下:

    Analyzing string 'abc'  
    Analyzing string '(null)'  
    Analyzing string ''  

5. C++例子

  在面试中经常用到的一个题目:
  已知memcpy的函数为:

  void* memcpy(void *dest , const void* src , size_t count)

  其中dest是目的指针,src是源指针。不调用c++/c的memcpy库函数,请编写memcpy。

    void* memcpy(void *dst, const void *src, size_t count)      
    {      
        //安全检查  
        assert( (dst != NULL) && (src != NULL) );      
      
        unsigned char *pdst = (unsigned char *)dst;      
        const unsigned char *psrc = (const unsigned char *)src;      
      
        //防止内存重复  
        assert(!(psrc<=pdst && pdst<psrc+count));      
        assert(!(pdst<=psrc && psrc<pdst+count));      
      
        while(count--)      
        {      
            *pdst = *psrc;      
            pdst++;      
            psrc++;      
        }      
        return dst;      
    }    

 

posted on 2015-12-18 15:21  夏天/isummer  阅读(586)  评论(0编辑  收藏  举报