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
实现。
Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.
LinkedIn