Linux下C语言程序开发18、自制try模块,教计算机处理8除0

 2024-03-18 05:04:16  阅读 0

上一节介绍了Linux操作系统中信号的概念。 我们知道崩溃错误通常伴随着信号的产生。 例如,分段错误引起的信号、除以0引起的信号等。

完整的程序开发过程_完全手册linux系统下c程序开发详解_程序开发模式

不仅如此,Linux中的信号还可以被“拦截”,甚至可以被修改来处理动作。 例如,上一节中,我们使用C语言编程来拦截信号,并将处理动作从默认的打印“错误”并退出更改为我们自定义的动作,因此程序在遇到分段错误时并没有退出。

try 语句中的错误处理

现在我们知道了 Linux 中信号的概念,我们实际上可以做一些更有趣的事情。 在讨论这个之前,我们先来看看当我们计算 8 除以 0 的值时会发生什么:

#encoding=utf8
if __name__=="__main__":
 a = 8/0
 print a

果然,无论用什么工具来计算8除0,都一样没有意义。 同样的道理,除数遇到0就会崩溃退出:

# python2 t.py 
Traceback (most recent call last):
 File "t.py", line 4, in 
 a = 8/0
ZeroDivisionError: integer division or modulo by zero

完全手册linux系统下c程序开发详解_完整的程序开发过程_程序开发模式

当然,在现实中,没有人会愚蠢到编写使用0作为除数的代码,但有时使用0作为除数是非常隐蔽的。 相信经常写代码的朋友一定都遇到过。

对于这种崩溃错误,try语句非常贴心地提供了。 现在使用try语句重写上面的代码,请看:

#encoding=utf8
if __name__=="__main__":
 try:
 a = 8/0
 except Exception, e:
 print 'ERROR: ', e
 print "python exit normally"

完整的程序开发过程_程序开发模式_完全手册linux系统下c程序开发详解

当你执行代码时,你会发现虽然无法计算出8/0,但程序避免了崩溃。 它首先提示“ERROR:or by zero”,然后打印出“exit”信息,正常退出程序。

# python2 t.py 
ERROR: integer division or modulo by zero
python exit normally

try 语法有助于编写更健壮的程序。 不幸的是,C语言没有这样的语法,但是在了解了Linux中信号的概念之后,我们可以自己用C语言实现一组try语句。

完整的程序开发过程_程序开发模式_完全手册linux系统下c程序开发详解

和功能

虽然C语言没有直接提供类似于中的try语句,但它确实提供了保存和恢复场景的功能。 结合我们已经了解的Linux中的信号机制,自己实现一套C语言的try语句并不容易。 多么困难的事情啊。

我们先看一下sum函数的说明:

完全手册linux系统下c程序开发详解_完整的程序开发过程_程序开发模式

并且函数非常适合解决低级错误或冲突。 它们可以保存堆栈上下文和环境表等信息。 稍后,函数可以使用这些信息,就像回到过去一样,修改程序的执行流程。 这话有点假。 让我们看一个例子。 请看下面的代码:

#include 
#include 
int main()
{
 jmp_buf mark;
 int ret = 0;
 ret = setjmp(mark);
 if(0==ret){
 printf("ret == 0 is true\n"); 
 longjmp(mark, -1);
 }else{
 printf("ret == 0 is false\n");
 }
 return 0;
}

分析这个程序。 它会打印“ret == 0 is true”还是“ret == 0 is false”? 编译执行,但是得到如下结果:

# ./a.out 
ret == 0 is true
ret == 0 is false

事实上,真实和虚假的信息都被打印了。 真是令人惊讶,现在我们来分析一下这个程序:

完整的程序开发过程_程序开发模式_完全手册linux系统下c程序开发详解

现在应该清楚了,可能有的朋友发现了,将这两个函数与Linux中的信号处理函数结合起来,是不是可以实现类似中的try语句呢?

使用C语言创建类似的try语句

由于该函数可以回到过去并修改历史记录,因此实现 try 语句太简单了。 仍然以8除0的计算为例,我们可以编写如下代码:

完全手册linux系统下c程序开发详解_程序开发模式_完整的程序开发过程

编译执行后发现,C语言程序不仅没有崩溃退出,还输出了“main exit”信息,而且还贴心地提示了“ERROR: by Zero”错误信息。

# ./a.out 
ERROR: division by zero
main exit normally

虽然功能大致实现了,但是上面的代码并不像try语法那么简洁。 这时候就可以利用宏封装对代码进行适当的调整。 请参见:

#include 
#include 
#include 
jmp_buf genv;
#define try if(({ \
 int __ret = 0; \
 __ret = sigsetjmp(genv,1); \
 0==__ret; \
 }))
#define except else
void signal_handle(int sig)
{
 siglongjmp(genv, -1);
}
int main()
{
 int ret = 0, a = 0;
 signal(SIGFPE, signal_handle);
 try{
 a = 8/0;
 }except{
 printf("ERROR: division by zero\n");
 }
 printf("main exit normally\n");
 return 0;
}

程序开发模式_完全手册linux系统下c程序开发详解_完整的程序开发过程

现在封装好了,是不是很像里面的try语句? 编译并执行,结果符合预期:

# ./a.out 
ERROR: division by zero
main exit normally

程序开发模式_完整的程序开发过程_完全手册linux系统下c程序开发详解

这样,我们就用C语言做了一个类似的try语句。 但它还是有局限性,比如genv全局变量的使用,而try只是简单的代码封装,不能嵌套。 再比如,这种封装会修改默认的信号处理功能,可能会给其他功能模块带来不便等,这些缺点当然是可以解决的。 由于篇幅限制,我们将在下一节或稍后继续讨论。

其实主要还是依赖栈的数据结构特性。 有兴趣的朋友可以自己先尝试一下,完全可以封装一个强大的C语言try库。

欢迎在评论区一起讨论、提问。 文章均为手写、原创。 他们每天深入浅出地介绍C语言、Linux等嵌入式开发。 如果你喜欢我的文章,就关注吧,你可以看到最新的更新和往期文章。

标签: c语言 try

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码