C 语言中的 try...catch
Try-Catch
机制在 Python、C++ 和 JavaScript 等许多编程语言中都很常见。一般结构如下。
try {
/*
Insert some lines of code that will probably give you errors
*/
}
catch {
/*
Write some code to handle the errors you're getting.
*/
}
它们允许你编写代码而无需测试每个语句。如果在 try
块中运行的程序遇到异常,则将异常传递给 catch
块。
如果异常与某些异常类型匹配,则执行 catch
块内的代码。否则,异常将传递回 try
块。
C 语言中的 Try-Catch
C 不支持异常处理。至少,它没有任何内置机制。
本指南将演示在 C 语言中提供 try-catch
功能的可能解决方案。应该注意,该解决方案不一定是完整的。
如果没有在遍历堆栈时释放内存的机制,异常处理系统是不完整和安全的,并且 C 没有垃圾收集器。我们可能还需要包含上下文管理器来释放内存。
该解决方案不打算提供完整而广泛的 try-catch
机制。这个概念在技术上可以用来处理一些异常。
我们将逐步构建解决方案,对代码进行更新。我们将使用 C 提供的两个函数,longjmp
和 setjmp
,它们可以从 setjmp.h
头文件中获得。
我们将仔细研究这两个函数的定义。
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
setjmp
接受一个 jmp_buf
类型的变量。直接调用此函数时,它返回 0
。
longjmp
接受两个变量,当使用相同的 jmp_buf
变量调用 longjmp
时,setjmp
函数返回与 longjmp
的第二个参数(val
)相同的值。
这里的 env
变量本质上是调用环境
,代表了寄存器的状态和函数调用时在代码中的位置。当调用 longjmp
时,调用环境
中的状态被复制到处理器,并返回存储在 longjmp
的 val
参数中的值。
对于一个简单的 Try-Catch
块,想法是将 Try
语句映射到 if
语句,然后 Catch
语句将成为条件的 else
。在这里,我们可以巧妙地利用 setjmp
可以返回不同值的事实。
如果函数返回 0
,那么我们知道唯一运行的代码是 TRY
块中的代码。如果函数返回任何其他内容,我们需要以与开始时相同的状态进入我们的 CATCH
块。
当我们 THROW
异常时,我们可以调用 longjmp
函数。
正如你将在下面的代码中看到的,我们还需要关闭 TRY
块。我们创建了一个 ENDTRY
函数,它提供了 do-while
块的结束部分。
这也有助于我们在同一个块中创建多个 TRY
语句。应该注意的是,它们不能嵌套,因为我们将重用 buf_state
变量。
下面是这个实现的一个例子。
#include <stdio.h>
#include <setjmp.h>
#define TRY do { jmp_buf buf_state; if ( !setjmp(buf_state)) {
#define CATCH } else {
#define ENDTRY }} while(0)
#define THROW longjmp(buf_state, 1)
int main() {
TRY {
printf("Testing Try statement \n");
THROW;
printf("Statement should not appear, as the THROW block has already thrown the exception \n");
}
CATCH {
printf("Got Exception \n");
}
ENDTRY;
return 0;
}
输出:
Testing Try statement
Got Exception
对于实际系统,这还不够。我们需要有不同类型的异常。
上面的例子只支持一种异常。再一次,我们可以使用 setjmp
的不同返回值。
代替使用 if-else
,我们将使用 switch-case
来切换它。
设计如下:TRY
语句将使用 switch
语句,CATCH
将是一个带有参数的宏,表示异常类型。每个 CATCH
语句的条件是它必须使用 break
关闭前一个 case
。
#include <stdio.h>
#include <setjmp.h>
#define TRY do { jmp_buf buf_state; switch(setjmp (buf_state)) { case 0:
#define CATCH(x) break; case x:
#define ENDTRY }} while(0)
#define THROW(x) longjmp(buf_state, x)
#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)
int main() {
TRY {
printf("Inside Try statement \n");
THROW(EXCEPTION2);
printf("This does not appear as exception has already been called \n");
}
CATCH(EXCEPTION1) {
printf("Exception 1 called \n");
}
CATCH(EXCEPTION2) {
printf("Exception 2 called \n");
}
CATCH(EXCEPTION3) {
printf("Exception 3 called \n");
}
ENDTRY;
return 0;
}
输出:
Inside Try statement
Exception 2 called
在 C 语言中将 Finally
添加到 Try-Catch
我们需要为完整的功能性 Try-Catch
实现添加一个 FINALLY
块。finally
块通常在 try
和 catch
块完成后执行。
无论是否抛出异常,它都会执行。
我们将如何实现这一点?关键思想是使用 switch
案例的 default
案例来实现 FINALLY
块。
但是,如果在正常情况下调用了异常,则 switch-case
将不会运行 default
情况。
我们将使用类似于 Duff’s Device 的机制。我们基本上将把 switch-case
语句与 do-while
语句交织在一起。
它的逻辑是这样的。
switch (an expression)
{
case 0: while (1) {
// code for case 0
break;
case 1:
// code for case 1
break;
}
default:
// code for default case
}
我们使用了 C 语言中最具争议的特性之一:在每个 case 标签之前开关不会自动断开。在这里,当调用 break
时,while
语句嵌套在 switch-case
中;它退出 while
循环并继续遍历案例。
在我们的代码上下文中(如下所示),switch
案例是我们所有的异常,default
案例在我们的 FINALLY
块中。自然,它会落入默认
情况,因为异常代码已经被执行。
这显示在下面的代码中。
#include <stdio.h>
#include <setjmp.h>
#define TRY do { jmp_buf buf_state; switch(setjmp (buf_state)) { case 0: while(1) {
#define CATCH(x) break; case x:
#define ENDTRY }} while(0)
#define THROW(x) longjmp(buf_state, x)
#define FINALLY break; } default:
#define EXCEPTION1 (1)
#define EXCEPTION2 (2)
#define EXCEPTION3 (3)
int main() {
TRY {
printf("Inside Try statement \n");
THROW(EXCEPTION2);
printf("This does not appear as exception has already been called \n");
}
CATCH(EXCEPTION1) {
printf("Exception 1 called \n");
}
CATCH(EXCEPTION2) {
printf("Exception 2 called \n");
}
CATCH(EXCEPTION3) {
printf("Exception 3 called \n");
}
FINALLY {
printf("This will always be called! \n");
}
ENDTRY;
return 0;
}
输出:
Inside Try statement
Exception 2 called
This will always be called!
用 C 语言制作 try-catch
系统的指南到此结束。当然,这里可能存在内存问题和一些限制(例如缺乏对嵌套 try-catch
系统的支持),但这是一个 C 语言中的功能性 try-catch
实现。
相关文章
在 C 语言中使用 typedef enum
发布时间:2023/05/07 浏览次数:181 分类:C语言
-
本文介绍了如何在 C 语言中使用 typedef enum。使用 enum 在 C 语言中定义命名整数常量 enum 关键字定义了一种叫做枚举的特殊类型。
C 语言中的 extern 关键字
发布时间:2023/05/07 浏览次数:114 分类:C语言
-
本文介绍了如何在 C 语言中使用 extern 关键字。C 语言中使用 extern 关键字来声明一个在其他文件中定义的变量
C 语言中的 #ifndef
发布时间:2023/05/07 浏览次数:186 分类:C语言
-
本文介绍了如何在 C 语言中使用 ifndef。在 C 语言中使用 ifndef 保护头文件不被多次包含 C 语言中的头文件用于定义同名源文件中实现的函数的接口。