C 中如何兼容并支持C语言中提供的异常处理机制
| 2006-05-09 | 作者:佚名 |
C 语言中如何处理goto语句
大家知道,在C语言程序中,goto语句被编译成机器指令后,它只对应一条jmp指令。但是在C 语言程序中,goto语句也会这么简单吗?no!为什么这么说呢?因为C 语言是面向对象的语言,如果goto语句只会简单地对应一条jmp指令,那么在许多情况下,这会破坏面向对象的一些特性。例如下面的示例程序,代码如下:
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
class MyTest
{
public:
MyTest ()
{
printf("构造一个MyTest类型的对象\n");
}
virtual ~ MyTest ()
{
printf("析构销毁一个MyTest类型的对象\n");
}
};
void main( void )
{
MyTest myobj0;
{
int error;
MyTest myobj1;
MyTest myobj2;
MyTest myobj3;
error = 1;
// 注意下面这条goto语句,如果它只是一条简单的jmp指令,
// 那么myobj1,myobj2,myobj3对象将如何被析构销毁呢?
if(error) goto Error;
printf("no error, continue\n");
}
Error:
return;
}
请编译运行一下,程序的运薪峁缦拢?br> 构造一个MyTest类型的对象
构造一个MyTest类型的对象
构造一个MyTest类型的对象
构造一个MyTest类型的对象
析构销毁一个MyTest类型的对象
析构销毁一个MyTest类型的对象
析构销毁一个MyTest类型的对象
析构销毁一个MyTest类型的对象
呵呵!从程序的运行结果来看,显然,它符合面向对象的规则定义,“一个对象被构造了,就必然会有析构的过程”。所以说,在C 语言中,它是能够很好兼容并支持goto语句的语义,这也与C 是C语言的继承、扩充、完善的版本等承诺是相一致的。但是同时我们也知道,C 中对象的析构,是由编译器来予以支持的,那就是当编译器在编译程序时,如果局部对象在离开它的作用域时,编译器会显式地插入一些调用对象析构函数的代码,来销毁这些即将无效掉的局部对象。但是程序中如果遭遇到goto语句时,显然,编译器也需要插入对局部对象的析构函数的显式调用。请在上面的程序中goto语句那一行,按F9设置一个断点;然后F5,调试程序;接着Alt 8切换到汇编代码的显示状态下,注意查看if(error) goto Error语句对应的汇编程序。截图如下:

呵呵!从上图可以很明显的看出,编译器在处理goto语句时,需要进行更多的工作,它必须要插入所有当前局部对象的析构函数的显式调用代码,然后才能真正执行jmp指令。其它对于许多其它类似的语句,编译器的处理也是类似,例如对于return语句的处理也是如此,把上面的那个程序小小改动一点,代码如下:
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
class MyTest
{
public:
MyTest ()
{
printf("构造一个MyTest类型的对象\n");
}
virtual ~ MyTest ()
{
printf("析构销毁一个MyTest类型的对象\n");
}
};
void main( void )
{
MyTest myobj0;
{
int error;
MyTest myobj1;
MyTest myobj2;
MyTest myobj3;
error = 1;
// 用return语句直接返回
if(error) return; //goto Error;
printf("no error, continue\n");
}
Error:
return;
}
同样也调试程序,接着Alt 8切换到汇编代码的显示状态下,注意查看if(error) return语句对应的汇编程序。截图如下:

总结
虽然说,在C 语言中,它能够很好兼容并支持goto语句的语义(也包括其它一些与异常处理相关的语句)。但是,主人公阿愚强烈建议程序员朋友在编写C 程序代码时,不要轻易使用goto语句,因为与C程序中的goto语句相比,它不仅破坏了结构化的程序设计,破坏了程序代码的整体美感,而且它更导致了C 程序模块的臃肿(编译器因此而导致需要插入了太多重复性代码)。