下载本文示例代码
在进入正式的讲解之前,先说几句废话。许多的编程新手对异常处理视而不见,程序里很少考虑异常情况。一部分人甚至根本就不考虑,以为程序总是能以正确的途径运行。譬如我们有的程序设计者调用fopen打开一个文件后,立马就开始进行读写操作,根本就不考虑文件是否正常打开了。这种习惯一定要改掉,纵使你再不愿意!这是软件健壮性的需要!异常处理不是浪费时间! 1.C语言异常处理 1.1 异常终止 标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于
头文件中。这两个函数本身不能检测异常,但在C程序发生异常后经常使用这两个函数进行程序终止。下面的这个例子描述了exit()的行为:
#include #include int main(void){ exit(EXIT_SUCCESS); printf("程序不会执行到这里\n"); return 0;} 在这个例子中,main函数一开始就执行了exit函数(此函数原型为void exit(int)),因此,程序不会输出"程序不会执行到这里"。程序中的exit(EXIT_SUCCESS)表示程序正常结束,与之对应的exit(EXIT_FAILURE)表示程序执行错误,只能强行终止。EXIT_SUCCESS、EXIT_FAILURE分别定义为0和1。 对于exit函数,我们可以利用atexit函数为exit事件"挂接"另外的函数,这种"挂接"有点类似Windows编程中的"钩子"(Hook)。譬如:
#include #include static void atExitFunc(void){ printf("atexit挂接的函数\n");}int main(void){ atexit(atExitFunc); exit(EXIT_SUCCESS); printf("程序不会执行到这里\n"); return 0;} 程序输出"atexit挂接的函数"后即终止。来看下面的程序,我们不调用exit函数,看看atexit挂接的函数会否执行:
#include #include static void atExitFunc(void){ printf("atexit挂接的函数\n");} int main(void){ atexit(atExitFunc); //exit(EXIT_SUCCESS); printf("不调用exit函数\n"); return 0;} 程序输出: 不调用exit函数 atexit挂接的函数 这说明,即便是我们不调用exit函数,当程序本身退出时,atexit挂接的函数仍然会被执行。 atexit可以被多次执行,并挂接多个函数,这些函数的执行顺序为后挂接的先执行,例如:
#include #include static void atExitFunc1(void){ printf("atexit挂接的函数1\n");}static void atExitFunc2(void){ printf("atexit挂接的函数2\n");}static void atExitFunc3(void){ printf("atexit挂接的函数3\n");}int main(void){ atexit(atExitFunc1); atexit(atExitFunc2); atexit(atExitFunc3); return 0;} 输出的结果是: atexit挂接的函数3 atexit挂接的函数2 atexit挂接的函数1 在Visual C 中,如果以abort函数(此函数不带参数,原型为void abort(void))终止程序,则会在debug模式运行时弹出如图1所示的对话框。
图1 以abort函数终止程序 1.2 断言(assert) assert宏在C语言程序的调试中发挥着重要的作用,它用于检测不会发生的情况,表明一旦发生了这样的情况,程序就实际上执行错误了,例如strcpy函数:
char *strcpy(char *strDest, const char *strSrc){ char *address = strDest; assert((strDest != NULL) && (strSrc != NULL)); while ((*strDest = *strSrc ) != ’\0’) ; return address;} 其中包含断言assert( (strDest != NULL) && (strSrc != NULL) ),它的意思是源和目的字符串的地址都不能为空,一旦为空,程序实际上就执行错误了,会引发一个abort。 assert宏的定义为:
#ifdef NDEBUG#define assert(exp) ((void)0)#else#ifdef __cplusplusextern "C"{ #endif _CRTIMP void __cdecl _assert(void *, void *, unsigned); #ifdef __cplusplus}#endif#define assert(exp) (void)( (exp) || (_assert(#exp, __FILE__, __LINE__), 0) )#endif /* NDEBUG */ 如果程序不在debug模式下,assert宏实际上什么都不做;而在debug模式下,实际上是对_assert()函数的调用,此函数将输出发生错误的文件名、代码行、条件表达式。例如下列程序:
#include #include #include char * myStrcpy( char *strDest, const char *strSrc ) { char *address = strDest; assert( (strDest != NULL) && (strSrc != NULL) ); while( (*strDest = *strSrc ) != ’\0’ ); return address;}int main(void){ myStrcpy(NULL,NULL); return 0;} 在此程序中,为了避免我们的strcpy与C库中的strcpy重名,将其改为myStrcpy。程序的输出如图2:
图2 assert的输出 失败的断言也会弹出如图1所示的对话框,这是因为_assert()函数中也调用了abort()函数。 一定要记住的是assert本质上是一个宏,而不是一个函数,因而不能把带有副作用的表达式放入assert的"参数"中。 1.3 errno errno在C程序中是一个全局变量,这个变量由C运行时库函数设置,用户程序需要在程序发生异常时检测之。C运行库中主要在math.h和stdio.h头文件声明的函数中使用了errno,前者用于检测数学运算的合法性,后者用于检测I/O操作中(主要是文件)的错误,例如:
#include #include #include int main(void){ errno = 0; if (NULL == fopen("d:\\1.txt", "rb")) { printf("%d", errno); } else { printf("%d", errno); } return 0;} 在此程序中,如果文件打开失败(fopen返回NULL),证明发生了异常。我们读取error可以获知错误的原因,如果D盘根目录下不存在"1.txt"文件,将输出2,表示文件不存在;在文件存在并正确打开的情况下,将执行到else语句,输出0,证明errno没有被设置。 Visual C 提供了两种版本的C运行时库。-个版本供单线程应用程序调用,另一个版本供多线程应用程序调用。多线程运行时库与单线程运行时库的一个重大差别就是对于类似errno的全局变量,每个线程单独设置了一个。因此,对于多线程的程序,我们应该使用多线程C运行时库,才能获得正确的error值。 另外,在使用errno之前,我们最好将其设置为0,即执行errno = 0的赋值语句。 1.4 其它 除了上述异常处理方式外,在C语言中还支持非局部跳转(使用setjmp和longjmp)、信号(使用signal、raise)、返回错误值或回传错误值给参数等方式进行一定能力的异常处理,但是其使用不如1.1~1.3节所介绍方式常用,我们不必过细研究。 从以上分析可知,C语言的异常处理是简单而不全面的。与C 的异常处理比起来,C语言异常处理相形见绌,它就像娘胎里的雏婴。共5页。 1 2 3 4 5 :
在进入正式的讲解之前,先说几句废话。许多的编程新手对异常处理视而不见,程序里很少考虑异常情况。一部分人甚至根本就不考虑,以为程序总是能以正确的途径运行。譬如我们有的程序设计者调用fopen打开一个文件后,立马就开始进行读写操作,根本就不考虑文件是否正常打开了。这种习惯一定要改掉,纵使你再不愿意!这是软件健壮性的需要!异常处理不是浪费时间! 1.C语言异常处理 1.1 异常终止 标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于头文件中。这两个函数本身不能检测异常,但在C程序发生异常后经常使用这两个函数进行程序终止。下面的这个例子描述了exit()的行为:
#include #include int main(void){ exit(EXIT_SUCCESS); printf("程序不会执行到这里\n"); return 0;} 在这个例子中,main函数一开始就执行了exit函数(此函数原型为void exit(int)),因此,程序不会输出"程序不会执行到这里"。程序中的exit(EXIT_SUCCESS)表示程序正常结束,与之对应的exit(EXIT_FAILURE)表示程序执行错误,只能强行终止。EXIT_SUCCESS、EXIT_FAILURE分别定义为0和1。 对于exit函数,我们可以利用atexit函数为exit事件"挂接"另外的函数,这种"挂接"有点类似Windows编程中的"钩子"(Hook)。譬如:
#include #include static void atExitFunc(void){ printf("atexit挂接的函数\n");}int main(void){ atexit(atExitFunc); exit(EXIT_SUCCESS); printf("程序不会执行到这里\n"); return 0;} 程序输出"atexit挂接的函数"后即终止。来看下面的程序,我们不调用exit函数,看看atexit挂接的函数会否执行:
#include #include static void atExitFunc(void){ printf("atexit挂接的函数\n");} int main(void){ atexit(atExitFunc); //exit(EXIT_SUCCESS); printf("不调用exit函数\n"); return 0;} 程序输出: 不调用exit函数 atexit挂接的函数 这说明,即便是我们不调用exit函数,当程序本身退出时,atexit挂接的函数仍然会被执行。 atexit可以被多次执行,并挂接多个函数,这些函数的执行顺序为后挂接的先执行,例如:
#include #include static void atExitFunc1(void){ printf("atexit挂接的函数1\n");}static void atExitFunc2(void){ printf("atexit挂接的函数2\n");}static void atExitFunc3(void){ printf("atexit挂接的函数3\n");}int main(void){ atexit(atExitFunc1); atexit(atExitFunc2); atexit(atExitFunc3); return 0;} 输出的结果是: atexit挂接的函数3 atexit挂接的函数2 atexit挂接的函数1 在Visual C 中,如果以abort函数(此函数不带参数,原型为void abort(void))终止程序,则会在debug模式运行时弹出如图1所示的对话框。
图1 以abort函数终止程序 1.2 断言(assert) assert宏在C语言程序的调试中发挥着重要的作用,它用于检测不会发生的情况,表明一旦发生了这样的情况,程序就实际上执行错误了,例如strcpy函数:
char *strcpy(char *strDest, const char *strSrc){ char *address = strDest; assert((strDest != NULL) && (strSrc != NULL)); while ((*strDest = *strSrc ) != ’\0’) ; return address;} 其中包含断言assert( (strDest != NULL) && (strSrc != NULL) ),它的意思是源和目的字符串的地址都不能为空,一旦为空,程序实际上就执行错误了,会引发一个abort。 assert宏的定义为:
#ifdef NDEBUG#define assert(exp) ((void)0)#else#ifdef __cplusplusextern "C"{ #endif _CRTIMP void __cdecl _assert(void *, void *, unsigned); #ifdef __cplusplus}#endif#define assert(exp) (void)( (exp) || (_assert(#exp, __FILE__, __LINE__), 0) )#endif /* NDEBUG */ 如果程序不在debug模式下,assert宏实际上什么都不做;而在debug模式下,实际上是对_assert()函数的调用,此函数将输出发生错误的文件名、代码行、条件表达式。例如下列程序:
#include #include #include char * myStrcpy( char *strDest, const char *strSrc ) { char *address = strDest; assert( (strDest != NULL) && (strSrc != NULL) ); while( (*strDest = *strSrc ) != ’\0’ ); return address;}int main(void){ myStrcpy(NULL,NULL); return 0;} 在此程序中,为了避免我们的strcpy与C库中的strcpy重名,将其改为myStrcpy。程序的输出如图2:
图2 assert的输出 失败的断言也会弹出如图1所示的对话框,这是因为_assert()函数中也调用了abort()函数。 一定要记住的是assert本质上是一个宏,而不是一个函数,因而不能把带有副作用的表达式放入assert的"参数"中。 1.3 errno errno在C程序中是一个全局变量,这个变量由C运行时库函数设置,用户程序需要在程序发生异常时检测之。C运行库中主要在math.h和stdio.h头文件声明的函数中使用了errno,前者用于检测数学运算的合法性,后者用于检测I/O操作中(主要是文件)的错误,例如:
#include #include #include int main(void){ errno = 0; if (NULL == fopen("d:\\1.txt", "rb")) { printf("%d", errno); } else { printf("%d", errno); } return 0;} 在此程序中,如果文件打开失败(fopen返回NULL),证明发生了异常。我们读取error可以获知错误的原因,如果D盘根目录下不存在"1.txt"文件,将输出2,表示文件不存在;在文件存在并正确打开的情况下,将执行到else语句,输出0,证明errno没有被设置。 Visual C 提供了两种版本的C运行时库。-个版本供单线程应用程序调用,另一个版本供多线程应用程序调用。多线程运行时库与单线程运行时库的一个重大差别就是对于类似errno的全局变量,每个线程单独设置了一个。因此,对于多线程的程序,我们应该使用多线程C运行时库,才能获得正确的error值。 另外,在使用errno之前,我们最好将其设置为0,即执行errno = 0的赋值语句。 1.4 其它 除了上述异常处理方式外,在C语言中还支持非局部跳转(使用setjmp和longjmp)、信号(使用signal、raise)、返回错误值或回传错误值给参数等方式进行一定能力的异常处理,但是其使用不如1.1~1.3节所介绍方式常用,我们不必过细研究。 从以上分析可知,C语言的异常处理是简单而不全面的。与C 的异常处理比起来,C语言异常处理相形见绌,它就像娘胎里的雏婴。共5页。 1 2 3 4 5 :
下载本文示例代码
Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析Visual C 中的异常处理浅析