程序出錯的處理

當一個程序在運行時崩潰,那你最希望看到什麼?他的錯誤信息,這樣你就可以對其進行修復,所以一個在出錯時毫無信息的程序那是很可怕的。因爲所有人都不能保證他的程序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的消息不能通過。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章