當一個程序在運行時崩潰,那你最希望看到什麼?他的錯誤信息,這樣你就可以對其進行修復,所以一個在出錯時毫無信息的程序那是很可怕的。因爲所有人都不能保證他的程序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的消息不能通過。
程序出錯的處理
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.