C语言异常与断言接口的实现
作者:网络转载 发布时间:[ 2015/8/31 13:18:09 ] 推荐标签:测试开发技术 C语言
程序中通常会出现三种错误:用户错误、运行期错误以及异常
标准库函数setjmp和longjmp
在C语言中,标准库函数setjmp和longjmp形成了结构化异常工具的基础。简单的说是setjmp实例化处理程序,而longjmp产生异常
setjmp和longjmp是C语言所独有的,它们部分弥补了C语言有限的转移能力。与刺激的abort()和exit()相比,goto语句看起来是处理异常的更可行方案。不幸的是,goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点(当然,除非你的所有代码都在main体中)。
为了解决这个限制,C函数库提供了setjmp()和longjmp()函数,它们分别承担非局部标号和goto作用。头文件<setjmp.h>申明了这些函数及同时所需的jmp_buf数据类型。
函数说明:
int setjmp(jmp_buf env) 建立本地的jmp_buf缓冲区并且初始化,用于将来跳转回此处。这个子程序保存程序的调用环境于env参数所指的缓冲区,env将被longjmp使用。如果是从setjmp直接调用返回,setjmp返回值为0。如果是从longjmp恢复的程序调用环境返回,setjmp返回非零值。
void longjmp(jmp_buf env, int value) 恢复env所指的缓冲区中的程序调用环境上下文,env所指缓冲区的内容是由setjmp子程序调用所保存。value的值从longjmp传递给setjmp。longjmp完成后,程序从对应的setjmp调用处继续执行,如同setjmp调用刚刚完成。如果value传递给longjmp零值,setjmp的返回值为1;否则,setjmp的返回值为value。
成员类型:
jmp_buf 数组类型,例如:struct int[16]或struct __jmp_buf_tag,用于保存恢复调用环境所需的信息。
jmp_buf 的定义:
typedef struct _jmp_buf
{
int _jp[_JBLEN+1];
} jmp_buf[1];
这个是 setjmp.h 里的一行定义,把一个 struct 定义成一个数组。这样,在声明 jmp_buf 的时候,可以把数据分配到堆栈上。但是作为参数传递的时候则作为一个指针。
原理非常简单:
1.setjmp(j)设置“jump”点,用正确的程序上下文填充jmp_buf对象j。这个上下文包括程序存放位置、栈和框架指针,其它重要的寄存器和内存数据。当初始化完jump的上下文,setjmp()返回0值。
2. 以后调用longjmp(j,r)的效果是一个非局部的goto或“长跳转”到由j描述的上下文处(也是到那原来设置j的setjmp()处)。当作为长跳转的目标而被调用时,setjmp()返回r或1(如果r设为0的话)。(记住,setjmp()不能在这种情况时返回0。)
通过有两类返回值,setjmp()让你知道它正在被怎么使用。当设置j时,setjmp()如你期望地执行;但当作为长跳转的目标时,setjmp()从外面“唤醒”它的上下文。你可以用longjmp()来终止异常,用setjmp()标记相应的异常处理程序。
本文地址:http://www.cnblogs.com/archimedes/p/c-exception-assert.html,转载请注明源地址。
一个简单的例子:
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second(void) {
printf("second
"); // 打印
longjmp(buf,1); // 跳回setjmp的调用处 - 使得setjmp返回值为1
}
void first(void) {
second();
printf("first
"); // 不可能执行到此行
}
int main() {
if ( ! setjmp(buf) ) {
first(); // 进入此行前,setjmp返回0
} else { // 当longjmp跳转回,setjmp返回1,因此进入此行
printf("main
"); // 打印
}
return 0;
}
运行结果:
second
main
在下例中,setjmp被用于包住一个例外处理,类似try。longjmp调用类似于throw语句,允许一个异常返回给setjmp一个异常值。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
void first(void);
void second(void);
static jmp_buf exception_env;
static int exception_type;
int main(void) {
void *volatile mem_buffer;
mem_buffer = NULL;
if (setjmp(exception_env)) {
/* 如果运行到这将产生一个异常*/
printf("first failed, exception type %d
", exception_type);
} else {
printf("calling first
");
first();
mem_buffer = malloc(300); /* 分配内存 */
printf("%s",strcpy((char*)mem_buffer, "first succeeded!")); /* ... 不会被执行 */
}
if (mem_buffer)
free((void*) mem_buffer); /* 小心释放内存 */
return 0;
}
void first(void) {
jmp_buf my_env;
printf("calling second
");
memcpy(my_env, exception_env, sizeof(jmp_buf));
switch (setjmp(exception_env)) {
case 3:
/* 如果运行到这,表示有异常 */
printf("second failed with type 3 exception; remapping to type 1.
");
exception_type = 1;
default:
memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
longjmp(exception_env, exception_type); /* continue handling the exception */
case 0:
/* normal, desired operation */
second();
printf("second succeeded
"); /* not reached */
}
memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
}
void second(void) {
printf("entering second
" );
exception_type = 3;
longjmp(exception_env, exception_type);
printf("leaving second
");
}
运行结果:
calling first
calling second
entering second
second failed with type 3 exception; remapping to type 1.
first failed, exception type 1
接口
Except接口在一系列宏指令和函数中包装了setjmp和longjmp,它们一起提供了一个结构化异常处理工具
异常是Except_T类型的一个全局或静态变量:
#ifndef EXCEPT_INCLUDED
#define EXCEPT_INCLUDED
#include <setjmp.h>
#define T Except_T
typedef struct T {
char *reason;
} T;
Except_T结构只有一个字段,它可以初始化为一个描述异常的字符串,当发生一个未处理的异常时,才把字符串打印出来
异常处理程序处理的是异常的地址。异常必须是全局的或静态的变量,因此它们的地址地标志了它们,异常e由宏指令引发或由函数引发:
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
void Except_raise(const T *e, const char *file,int line);
处理程序是由TRY-EXCEPT和TRY-FINALLY语句来实例化的,这两个语句用宏指令实现,这两个语句可以处理嵌套异常,也可以管理异常状态的数据
TRY-EXCEPT的语法是:
TRY
S
EXCEPT(e1)
S1
EXCEPT(e2)
S2
……
EXCEPT(en)
Sn
ELSE
S0
END_TRY
看下面的代码:
int Allocation_handle = 0;
jmp_buf Allocate_Failed;
void *allocate(unsigned n)
{
void *new = malloc(n);
if(new)
return new;
if(Allocation_handle)
longjmp(Allocate_Failed, 1);
assert(0);
}
char *buf;
Allocation_handle = 1;
if(setjmp(Allocate_Failed)) {
fprintf(stderr, "cound't allocate the buff
");
exit(EXIT_FAILURE);
}
buf = allocate(4096);
Allocation_handle = 0;
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11