assert定义和用法

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

使用断言可以创建更稳定,品质更好且不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。 单元测试必须使用断言(Junit/JunitX)。
除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。
使用断言使我们向按契约式设计更近了一步。

断言特性

编辑
前置条件断言:代码执行之前必须具备的特性
后置条件断言:代码执行之后必须具备的特性
前后不变断言:代码执行前后不能变化的特性

使用方式

编辑
断言可以有两种形式
1.assert Expression1
2.assert Expression1:Expression2
其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。

java断言

编辑
断言在默认情况下是关闭的,要在编译时启用断言,需要使用source1.4标记 即javac source1.4 Test.java ,在运行时启用断言需要使用 -ea参数 。要在系统类中启用和禁用断言可以使用 -ea和 -dsa参数。
例如:
1
2
3
4
5
6
7
8
9
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;
这个断言的副作用是因为它修改了程序中变量的值并且未抛出错误,这样的错误如果不细心的检查是很难发现的。但是同时我们可以根据以上的副作用得到一个有用的特性,根据它来测试断言是否打开。
1
2
3
4
5
6
7
8
9
public  class  AssertExampleTwo{
     public  static  void  main(String args[]){
         boolean  isEnable= false ;
         assert  isEnable= true ;
         if (isEnable== false ){
             throw  newRuntimeException( "Assertion should be enable!" );
         }
     }
}

使用断言

编辑
1.可以在预计正常情况下程序不会到达的地方放置断言 :assert false
2.断言可以用于检查传递给私有方法的参数。(对于公有方法,因为是提供给外部的接口,所以必须在方法中有相应的参数检验才能保证代码的健壮性)
3.使用断言测试方法执行的前置条件和后置条件
4.使用断言检查类的不变状态,确保任何情况下,某个变量的状态必须满足。(如age属性应大于0小于某个合适值)
不用断言
断言语句不是永远会执行,可以屏蔽也可以启用
因此:
1.不要使用断言作为公共方法的参数检查,公共方法的参数永远都要执行
2.断言语句不可以有任何边界效应,不要使用断言语句去修改变量和改变方法的返回值.
C里的宏
宏名: assert
功 能: 测试一个条件并可能使程序终止
用 法: void assert(int test);
程序例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
1
2
3
4
5
6
#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:
1
2
3
4
5
6
7
8
9
10
11
12
13
#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;
}
<think>我们正在讨论C语言中的assert()宏。根据引用[1][2][3][4],assert宏用于在程序中插入断言,即检查某个条件是否满足。如果条件满足(表达式为真),则程序继续执行;如果条件不满足(表达式为假),则程序终止并输出错误信息。 功能:用于调试,帮助开发者定位程序中的错误假设。 使用步骤: 1. 包含头文件:<assert.h> 2. 在需要检查的地方使用assert(表达式); 注意:assert是一个宏,不是函数。在发布版本中通常可以通过定义NDEBUG宏来禁用assert,这样断言就不会被执行。 使用示例: 假设我们有一个函数,要求传入的指针不为NULL,我们可以使用assert来检查。 示例代码: */ #include <stdio.h> #include <assert.h> void process_array(int *arr, int size) { // 断言:arr不为空指针 assert(arr != NULL); // 断言:size大于0 assert(size > 0); // 处理数组的代码 for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } } int main() { int my_array[] = {1, 2, 3, 4, 5}; process_array(my_array, 5); // 下面这个调用会触发断言失败 // process_array(NULL, 5); // 如果取消注释,程序会终止 return 0; } /* 在这个示例中,如果调用process_array时传入NULL指针,程序会立即终止,并输出错误信息,包括文件名、行号失败的表达式。 注意事项: 1. assert主要用于调试,不应该用于检查程序在正常运行时可能发生的错误(如用户输入错误、文件不存在等),因为这些错误应该在发布版本中处理,而assert在发布版本中可能被禁用。 2. 使用assert可以帮助在开发阶段捕获程序逻辑错误。 根据引用[3][4],assert在条件为假时,会调用abort()终止程序,并输出错误信息到标准错误流stderr。错误信息通常包括:表达式、文件名、行号。 因此,合理使用assert可以提高代码的健壮性,并帮助快速定位问题。 相关问题: 1. assert宏在发布版本中如何禁用? 2. 除了assert,C语言还有哪些错误处理机制? 3. 使用assert时需要注意哪些问题?</think>### C语言 `assert()` 宏详解 `assert()` 是 C 语言中用于调试的核心宏,定义在 `<assert.h>` 头文件中。其核心功能是**验证程序中的假设条件**:若条件成立则继续执行;若条件不成立则终止程序并输出错误信息[^3][^4]。 #### 一、作用与原理 1. **功能** - 当表达式为真(非零)时:忽略断言,程序继续执行 - 当表达式为假(零)时: ✓ 终止程序执行 ✓ 输出错误信息(文件名、行号、失败表达式) ✓ 调用 `abort()` 结束进程[^1][^4] ```c // 原型 void assert(int expression); ``` 2. **设计目的** - 捕捉程序中的逻辑错误非法假设(如空指针、越界索引) - 仅用于调试阶段,发布版本可通过 `NDEBUG` 宏禁用[^2][^4] #### 二、使用示例 ```c #include <stdio.h> #include <assert.h> // 示例1:验证指针非空 void safe_print(char* str) { assert(str != NULL); // 若str为NULL则终止 printf("String: %s\n", str); } // 示例2:验证数组索引有效性 int get_element(int arr[], int size, int index) { assert(index >= 0 && index < size); // 索引越界检查 return arr[index]; } int main() { char text[] = "Hello"; safe_print(text); // 正常执行 // 触发断言失败(测试用) // safe_print(NULL); // 输出: test.c:4: safe_print: Assertion 'str != NULL' failed int nums[3] = {10, 20, 30}; printf("Value: %d\n", get_element(nums, 3, 1)); // 输出20 // get_element(nums, 3, 5); // 触发索引断言 return 0; } ``` **输出错误示例**(当 `safe_print(NULL)` 被调用时): ``` assertion.c:4: safe_print: Assertion 'str != NULL' failed. Aborted (core dumped) ``` #### 三、关键注意事项 1. **禁用断言(发布环境)** 在编译前定义 `NDEBUG` 宏可移除所有断言: ```bash gcc -DNDEBUG program.c # 发布模式编译 ``` 2. **使用原则** - ✔️ 检查**不可能发生**的条件(如内部逻辑错误) - ❌ 不用于处理预期错误(如用户输入、文件I/O失败) - 避免在断言中调用有副作用的函数: ```c assert(printf("test") == 4); // 错误!发布模式会移除该语句 ``` 3. **与错误处理的区别** | 机制 | 适用场景 | 是否可恢复 | |-------------|-------------------|------------| | `assert()` | 程序逻辑错误 | 不可恢复 | | `errno` | 运行时外部错误 | 可恢复 | | 返回值检查 | 预期内的操作失败 | 可恢复 | > **总结**:`assert()` 是开发阶段的“程序警察”,通过立即暴露非法条件加速调试,但需在发布版本中禁用[^3][^4]。 --- ### 相关问题 1. 如何在 C 语言中实现自定义断言(覆盖 `assert()` 的默认行为)? 2. `assert()` 与异常处理(如 C++ 的 `try/catch`)有何本质区别? 3. 为什么在断言表达式中使用函数调用可能导致未定义行为? 4. 在多线程环境下使用 `assert()` 需要注意哪些问题? [^1]: 【C语言指南】assert宏 使用详解 [^2]: C语言:assert宏的用法 [^3]: C语言中的assert [^4]: 【C语言】assert(断言)使用详解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值