程序出错的处理

当一个程序在运行时崩溃,那你最希望看到什么?他的错误信息,这样你就可以对其进行修复,所以一个在出错时毫无信息的程序那是很可怕的。因为所有人都不能保证他的程序100%的正确,即使程序完全正确,系统出错也很有可能导致程序相出错,所以,当程序出错时,应给出相应的提示信息,以便对其进行修复。

使用assert宏
    assert宏定义在assert.h头文件中,其原型为:
        #include<assert,h>
        void assert(int expression);
    他先计算表达式,若表达式的值为0,也就是假,将向srderr时输出一条信息,然后调用abort使程序停止运行,例如如下程序:
    #include <assert.h>
    #include <stdio.h>
   
    int main(void)
    {
            FILE *f;
       
        f = fopen("123", "r");
            assert(f);

            return 0;
    }
    除非你的目录下有123这个文件否则,就会出现下面的信息:
    a.out: 1.c:8: main: Assertion `f' failed.
    已放弃
    另外,如果你使用  assert((f = fopen("123","r")));的话,那么当你,想用#define NDEBUG 来去掉assert的断言时,你就完了,因为(f = fopen("123","r")根本不会去执行,如果以后你还用这个文件的话,那错误就不在是,文件打开失败那么简单得了。所以,最好是将语句与条件分开来写。但是,大量使用assert断言,将会使你的程序变得很慢很慢,所以,不要将断言弄的满屏幕都是,除非你在作纯粹的测试是可以的。
    还有两个宏很不错,__LINE__和__FILE__,他们能指出他们所在的行和所在的文件。我们将我们来打造一个更加安全的文件打开函数:
    int fileopen(FILE ** f, const char * n, const char *m, const int l, const char *fn)
    {
        if (NULL == (*f = fopen(n,m)))
        {
            fprintf(stderr,"file:%s line : %d open file %s failed!\n",fn, l, n);
            return 0;
        }
        return 1;
    }
    出错后的提示为:file:1.c line : 9 open file 123 failed!,很明显在1.c文件的第9行,打开文件出错。如果我们加上GNU C 扩展的__FUNCTION__宏,则还可以锁定具体的函数,例如,将上面的fileopen改为:
int fileopen(FILE ** f, const char * n, const char *m, const int l, const char *fn, const char * fun)
    {
        if (NULL == (*f = fopen(n,m)))
        {
            fprintf(stderr,"file:%s line : %d function :%s open file %s failed!\n",fn, l, fun, n);
            return 0;
        }
        return 1;
    }
    调用时加上__FUNCTION__就行了,file:1.c line:9 functon: main open file 123 failed!这样,信息就会更加的详细一点,尤其是当调用关系很复杂的情况下,这个宏很管用。
    另外,还有几个常用的函数来支持错误的处理,还有一个很用的变量来使用,现介绍一下配角,虽说配角,但只是在出错的时候。
    void    clearerr    (FILE * stream);    清除EOF条件,以及任何为stream所设置的错误标志。
    int     feof        (FILE * stream);    如果stream设置了EOF标志则返回真。
    int     ferror  (FILE * stream);    如果设置了出错标志就返回真。
   
    errno是一个变量,所有的函数都能对其操作,但没有那个库函数将其清零,所以,在使用的是后应现清零后使用。例如:
    #include <assert.h>
    #include <stdio.h>
    #include <errno.h>
    #include <math.h>
   
    int main(void)
    {
            int i;
            i =(int)sqrt((double)(-1));
            if (errno)
                    perror("Wrong\n");
            else
                    printf("%d",i);
            return 0;
    }
    这是一个必然出错的程序,因为sqrt的取之不能为负数,(虽然数学上可以)由于我们使用了errno,所以其输出将会是:
    Wrong
    : Numerical argument out of domain
    参数超出范围,这样的结果很明显,如果配合前面所说的几个宏来说,则会更好。另外值得要说的是,在连接math.h的时候要加上-l m参数;
    变量errno的值
    #define EPERM              1   /* Operation not permitted */
    #define ENOENT          2   /* No such file or directory */
    #define ESRCH              3   /* No such process */
    #define EINTR              4   /* Interrupted system call */
    #define EIO                 5   /* I/O error */
    #define ENXIO              6   /* No such device or address */
    #define E2BIG              7   /* Arg list too long */
    #define ENOEXEC         8   /* Exec format error */
    #define EBADF              9   /* Bad file number */
    #define ECHILD              10  /* No child processes */
    #define EAGAIN              11  /* Try again */
    #define ENOMEM          12  /* Out of memory */
    #define EACCES              13  /* Permission denied */
    #define EFAULT              14  /* Bad address */
    #define ENOTBLK         15  /* Block device required */
    #define EBUSY               16  /* Device or resource busy */
    #define EEXIST              17  /* File exists */
    #define EXDEV               18  /* Cross-device link */
    #define ENODEV          19  /* No such device */
    #define ENOTDIR         20  /* Not a directory */
    #define EISDIR              21  /* Is a directory */
    #define EINVAL              22  /* Invalid argument */
    #define ENFILE              23  /* File table overflow */
    #define EMFILE              24  /* Too many open files */
    #define ENOTTY              25  /* Not a typewriter */
    #define ETXTBSY         26  /* Text file busy */
    #define EFBIG               27  /* File too large */
    #define ENOSPC              28  /* No space left on device */
    #define ESPIPE              29  /* Illegal seek */
    #define EROFS               30  /* Read-only file system */
    #define EMLINK              31  /* Too many links */
    #define EPIPE               32  /* Broken pipe */
    #define EDOM                33  /* Math argument out of domain of func */
    #define ERANGE          34  /* Math result not representable */
    #define EDEADLK         35  /* Resource deadlock would occur */
    #define ENAMETOOLONG    36  /* File name too long */
    #define ENOLCK              37  /* No record locks available */
    #define ENOSYS              38  /* Function not implemented */
    #define ENOTEMPTY           39  /* Directory not empty */
    #define ELOOP               40  /* Too many symbolic links encountered */
    #define EWOULDBLOCK     EAGAIN  /* Operation would block */
    #define ENOMSG          42  /* No message of desired type */
    #define EIDRM               43  /* Identifier removed */
    #define ECHRNG          44  /* Channel number out of range */
    #define EL2NSYNC            45  /* Level 2 not synchronized */
    #define EL3HLT              46  /* Level 3 halted */
    #define EL3RST              47  /* Level 3 reset */
    #define ELNRNG          48  /* Link number out of range */
    #define EUNATCH         49  /* Protocol driver not attached */
    #define ENOCSI              50  /* No CSI structure available */
    #define EL2HLT              51  /* Level 2 halted */
    #define EBADE               52  /* Invalid exchange */
    #define EBADR               53  /* Invalid request descriptor */
    #define EXFULL              54  /* Exchange full */
    #define ENOANO          55  /* No anode */
    #define EBADRQC         56  /* Invalid request code */
    #define EBADSLT         57  /* Invalid slot */
    #define EDEADLOCK           58  /* File locking deadlock error */
    #define EBFONT              59  /* Bad font file format */
    #define ENOSTR              60  /* Device not a stream */
    #define ENODATA         61  /* No data available */
    #define ETIME               62  /* Timer expired */
    #define ENOSR               63  /* Out of streams resources */
    #define ENONET          64  /* Machine is not on the network */
    #define ENOPKG          65  /* Package not installed */
    #define EREMOTE         66  /* Object is remote */
    #define ENOLINK         67  /* Link has been severed */
    #define EADV                68  /* Advertise error */
    #define ESRMNT          69  /* Srmount error */
    #define ECOMM               70  /* Communication error on send */
    #define EPROTO              71  /* Protocol error */
    #define EMULTIHOP           72  /* Multihop attempted */
    #define EDOTDOT         73  /* RFS specific error */
    #define EBADMSG         74  /* Not a data message */
    #define EOVERFLOW           75  /* Value too large for defined data type */
    #define ENOTUNIQ            76  /* Name not unique on network */
    #define EBADFD              77  /* File descriptor in bad state */
    #define EREMCHG         78  /* Remote address changed */
    #define ELIBACC         79  /* Can not access a needed shared library */
    #define ELIBBAD         80  /* Accessing a corrupted shared library */
    #define ELIBSCN         81  /* .lib section in a.out corrupted */
    #define ELIBMAX         82  /* Attempting to link in too many shared libraries */
    #define ELIBEXEC            83  /* Cannot exec a shared library directly */
    #define EILSEQ              84  /* Illegal byte sequence */
    #define ERESTART            85  /* Interrupted system call should be restarted */
    #define ESTRPIPE            86  /* Streams pipe error */
    #define EUSERS              87  /* Too many users */
    #define ENOTSOCK            88  /* Socket operation on non-socket */   
    #define EDESTADDRREQ        89  /* Destination address required */
    #define EMSGSIZE            90  /* Message too long */
    #define EPROTOTYPE      91  /* Protocol wrong type for socket */
    #define ENOPROTOOPT     92  /* Protocol not available */
    #define EPROTONOSUPPORT 93  /* Protocol not supported */
    #define ESOCKTNOSUPPORT 94  /* Socket type not supported */
    #define EOPNOTSUPP      95  /* Operation not supported on transport endpoint */
    #define EPFNOSUPPORT        96  /* Protocol family not supported */
    #define EAFNOSUPPORT        97  /* Address family not supported by protocol */
    #define EADDRINUSE      98  /* Address already in use */
    #define EADDRNOTAVAIL   99  /* Cannot assign requested address */
    #define ENETDOWN            100 /* Network is down */
    #define ENETUNREACH     101 /* Network is unreachable */
    #define ENETRESET           102 /* Network dropped connection because of reset */
    #define ECONNABORTED        103 /* Software caused connection abort */
    #define ECONNRESET      104 /* Connection reset by peer */
    #define ENOBUFS         105 /* No buffer space available */
    #define EISCONN         106 /* Transport endpoint is already connected */
    #define ENOTCONN            107 /* Transport endpoint is not connected */
    #define ESHUTDOWN       108 /* Cannot send after transport endpoint shutdown */
    #define ETOOMANYREFS        109 /* Too many references: cannot splice */
    #define ETIMEDOUT           110 /* Connection timed out */
    #define ECONNREFUSED        111 /* Connection refused */
    #define EHOSTDOWN       112 /* Host is down */
    #define EHOSTUNREACH        113 /* No route to host */
    #define EALREADY            114 /* Operation already in progress */
    #define EINPROGRESS     115 /* Operation now in progress */
    #define ESTALE              116 /* Stale NFS file handle */
    #define EUCLEAN         117 /* Structure needs cleaning */
    #define ENOTNAM         118 /* Not a XENIX named type file */
    #define ENAVAIL         119 /* No XENIX semaphores available */
    #define EISNAM              120 /* Is a named type file */
    #define EREMOTEIO           121 /* Remote I/O error */
    #define EDQUOT          122 /* Quota exceeded */

    abort()函数
    这个函数用起来很简单,只要abort()一下就可以了,但他作的更加简单,啥都不做,直接退出程序,告诉操作系统这是一个不正常的终止。如果没有ulimit的限制,abort还会卸下一个core文件。
    exit()函数
    相对于abort来说,exit的工作实在是太多了,和abort一样是退出程序,但exit会在完成清理工作之后才正式的退出,如果你用atexit注册了函数,用exit退出时,还将逆序调用这些注册函数,以进行程序的扫尾工作。
    exit没有返回值,但他有一个参数,一个返回给OS 的退出值。理论上来说所有的整数都是合法的,但是标准库终止定义了EXIT_SUCCESS和EXIT_FAILURE两个用于退出的宏。而0是一个可移植的退出值。
    atexit()函数
    这个函数用来注册在程序退出时要完成的工作由哪些函数来作,其原型:
    #include <stdlib.h>
    int atexit(void(*function)(void));
    如果函数注册成功,则返回0,否则返回1。
    例如:
    #include <stdio.h>
    #include <stdlib.h>
    void printexit(void);
    int main(void)
    {
            if (atexit(printexit))
            {
                    printf("Failed\n");
                    exit(EXIT_FAILURE);
            }

            printf("exiting...\n");
            return 0;
    }

    void printexit(void)
    {
            printf("exit the program.\n");
    }
    整个程序并没有明显的调用printexit的地方,只有在atexit函数那里注册了一下,所以在程序return之后,就会执行printexit。
    exiting...
    exit the program.

    strerror()函数
    当错误返回是,并不是所有的人的都能看得懂错误的代码是什么,所以,就要将它转换成可读的字符串,strerror就是这个功能,我们只要将errno传给他就会返回对应的错误信息,而不在是简简单单的数字。
    perror()函数
    #include <stdio.h>
    #include <errno.h>
    void perror(const char *s);
    他能够打印错误信息,并输出到stderr中。

利用系统日志进行出错处理
    syslog的日志级别
        #define LOG_EMERG       0   /* system is unusable */
        #define LOG_ALERT       1   /* action must be taken immediately */
        #define LOG_CRIT        2   /* critical conditions */
        #define LOG_ERR     3   /* error conditions */
        #define LOG_WARNING 4   /* warning conditions */
        #define LOG_NOTICE  5   /* normal but significant condition */
        #define LOG_INFO        6   /* informational */
        #define LOG_DEBUG       7   /* debug-level messages */
    功能值:
        #define LOG_KERN        (0<<3)  /* kernel messages */
        #define LOG_USER        (1<<3)  /* random user-level messages */
        #define LOG_MAIL        (2<<3)  /* mail system */
        #define LOG_DAEMON  (3<<3)  /* system daemons */
        #define LOG_AUTH        (4<<3)  /* security/authorization messages */
        #define LOG_SYSLOG  (5<<3)  /* messages generated internally by syslogd */
        #define LOG_LPR     (6<<3)  /* line printer subsystem */
        #define LOG_NEWS        (7<<3)  /* network news subsystem */
        #define LOG_UUCP        (8<<3)  /* UUCP subsystem */
        #define LOG_CRON        (9<<3)  /* clock daemon */
        #define LOG_AUTHPRIV    (10<<3) /* security/authorization messages (private) */
        #define LOG_FTP     (11<<3) /* ftp daemon */
    系统日志函数
        #include <syslog.h>
        void    syslog  (int priority, const char *format, ...);
        其中,priority是级别与功能值相或的结果。format是写入的字符串,类似于printf的格式,%m表示,由strerror(errno)所生成的错误信息串。例如上面sqrt的错误,写入日志的话:
        syslog(LOG_WARNING | LOG_USER, "%s %m",__FUNCTION__);
        另外,由于LOG_USER是默认的级别,所以可以省略不写。
        syslog(LOG_WARNING, "%s %m",__FUNCTION__);
    使用openlog()来定制日志
        #include <syslog.h>
        void    openlog (const char * ident, int option, int facility);
        ident是加到日志消息前面的字符串,option是下表中0个或多个选项的或值,facility是级别值
            #define LOG_PID     0x01    /* log the pid with each message */
            #define LOG_CONS        0x02    /* log on the console if errors in sending */
            #define LOG_ODELAY  0x04    /* delay open until first syslog() (default) */
            #define LOG_NDELAY  0x08    /* don't delay open */
            #define LOG_NOWAIT  0x10    /* don't wait for console forks: DEPRECATED */
            #define LOG_PERROR  0x20    /* log to stderr as well */
        如果你不调用他,syslog会在你第一次调用syslog函数的时候自动调用他,closelog用来关闭openlog打开的文件描述符。
            void    closelog    (void);
        例如:
            openlog("my log", LOG_PID, LOG_USER);
            syslog(LOG_NOTICE, "Nothing\n");
    调用setlogmask为所有的日志消息设置默认级别
        int setlogmask(int priority);
        参数priority不是单个优先级就是优先级范围,函数设置了优先级的默认掩码,syslog拒绝任何没有在掩码中设置优先级的消息,为了简化优先级掩的设置,<syslog.h>定义了两个比较有用的宏。
        LOG_MASK(int priority);
        LOG_UPTO(int priority);
        前者创建一个仅由一个优先级组成的掩码,priority作为参数传递,后者创建一个由一系列降序组成的掩码,这里的priority是允许的最低的优先级,例如,LOG_UPTO(LOG_NOTICE)创建的掩码包含从LOG_EMERG到LOG_NOTICE之间的任何级别的消息。而LOG_INFO LOG_DEBUG的消息不能通过。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值