《C编程.中级C》4.错误处理

C不提供对错误处理的直接支持(也称为异常处理)。按照惯例,程序员应该首先防止错误发生,并测试函数的返回值。例如,-1和NULL分别用于几个函数,如socket()(Unix套接字编程)或malloc(),以指示程序员应该注意的问题。在最糟糕的情况下,存在不可避免的错误并且无法从中恢复,C程序员通常会尝试记录错误并“正常”终止程序。

在包含<errno.h>之后,程序可以访问一个名为“errno”的外部变量 – 该文件来自某些操作系统中可能出现的错误的定义(例如Linux – 在这种情况下,定义是在include / asm-generic / errno.h)当程序要求资源时。这样的变量索引错误描述可由函数’strerror(errno)’访问。

以下代码测试库函数malloc的返回值,以查看动态内存分配是否正确完成:

#include  <stdio.h> / * perror * /
#include  <errno.h> / * errno * /
#include  <stdlib.h> / * malloc,free,exit * /

int  main (void )
{

    / *指向char的指针,请求动态分配2,000,000,000个
     *存储元素(声明为
     * unsigned long int 
类型的整数常量)。(如果你的系统内存少于2 GB      *,那么对malloc的调用将失败。)
     * / 
    char  * ptr  =  malloc (2000000000UL );

    if  (ptr  ==  NULL ) { 
        perror (“malloc failed” ); 
        / *在这里你可能想要退出程序或补偿
           你没有2GB可用
         * / 
    }  否则 { 
        / *此后的其余代码可以假设
         成功分配
了2,000,000,000个*字符...          * / 
        免费(ptr ); 
    }

    退出(EXIT_SUCCESS );  / *退出程序* / 
}

上面的代码片段显示了使用库函数malloc的返回值来检查错误。许多库函数都有返回值来标记错误,因此应该由精明的程序员检查。在上面的代码片段中,从malloc返回的NULL指针表示分配错误,因此程序退出。在更复杂的实现中,程序可能会尝试处理错误并尝试从失败的内存分配中恢复。

防止零除错误

C程序员常见的缺陷是在除法命令之前没有检查除数是否为零。以下代码将产生运行时错误,在大多数情况下,退出。

int  dividend  =  50 ; 
int  divisor  =  0 ; 
int  商;

quotient  =  (dividend / divisor );  / *这会产生运行时错误!* /

由于超出本文档范围的原因,您必须检查或确保除数从不为零。或者,对于* nix进程,您可以通过阻止SIGFPE信号来阻止操作系统终止进程。

下面的代码通过在分割之前检查除数是否为零来解决此问题。

#include  <stdio.h> / *表示fprintf和stderr * /
#include  <stdlib.h> / * for exit * /
int  main ( void  )
{ 
    int  dividend  =  50 ; 
    int  divisor  =  0 ; 
    int  商;

    if  (divisor  ==  0 ) { 
        / *处理此错误的示例。将消息写入stderr,然后
         *以失败退出。
         * / 
        fprintf (stderr , “除以零!中止...... \ n ” ); 
        退出(EXIT_FAILURE );  / *表示失败。* / 
    }

    quotient  =  dividend  /  divisor ; 
    退出(EXIT_SUCCESS );  / *表示成功。* / 
}

信号

在某些情况下,环境可以通过提高信号来响应C中的编程错误。信号是由主机环境或操作系统引发的事件,用于指示已发生特定错误或严重事件(例如,除以零,中断等)。但是,这些信号并不意味着用作错误捕捉; 它们通常表示会干扰正常程序流程的关键事件。

要处理信号,程序需要使用signal.h头文件。需要定义信号处理程序,然后调用signal()函数以允许处理给定信号。在代码中引发异常的一些信号(例如,除以零)不太可能允许程序恢复。这些信号处理程序将需要确保在程序终止之前正确清理某些资源。

此示例创建信号处理程序并引发信号:

#include  <signal.h>
#include  <stdio.h>
#include  <stdlib.h>

static  void  catch_function (int  signal ) { 
    puts (“Interactive attention signal caught。” ); 
}

int  main (void ) { 
    if  (signal (SIGINT , catch_function ) ==  SIG_ERR ) { 
        fputs (“设置信号处理程序时发生错误。\ n ” , stderr ); 
        返回 EXIT_FAILURE ; 
    } 
    puts (“提高交互式注意信号。” ); 
    if  (raise (SIGINT ) !=  0 ) { 
        fputs (“引发信号错误。\ n “ , stderr ); 
        返回 EXIT_FAILURE ; 
    } 
    puts (”退出。“ ); 
    返回 0 ; 
}

setjmp 

该setjmp的函数可用于模拟其他编程语言的异常处理功能。第一次调用setjmp提供了返回给定函数的引用点,只要包含setjmp()的函数没有返回或退出,它就是有效的。对longjmp的调用会导致执行返回到关联的setjmp调用点。

#include  <stdio.h>
#include  <setjmp.h>

jmp_buf  test1 ;

空隙 tryjump ()
{ 
    longjmp的(TEST1 , 3 ); 
}

int  main  (void )
{ 
    if  (setjmp (test1 )== 0 ) { 
        printf  (“setjmp()return 0.” ); 
        tryjump (); 
    }  else  { 
        printf  (“setjmp从longjmp函数调用返回。” ); 
    } 
}

当setjmp从longjmp调用返回时,非易失性变量的值可能会被破坏。

虽然setjmp()和longjmp()可用于错误处理,但如果可能,通常最好使用函数的返回值来指示错误。

猜你想读:《C编程.中级C》5.流I/O

THE END
分享