C 语言中的 try...catch
C 语言中的 Try-Catch
在 C 语言中将 Finally 添加到 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
#include
#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
#include
#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
#include
#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 实现。
Enjoying our tutorials? Subscribe to DelftStack on YouTube to support us in creating more high-quality video guides. Subscribe