C 语言中的 try...catch

简介: C 语言中的 Try-Catch 在 C 语言中将 Finally 添加到 Try-Catch Try-Catch 机制在 Python、C++ 和 JavaScript 等许多编程语言中都很常见。一般结构如下。 try { /*

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