1、C语言的错误处理
其它编程语言中,例如,C++,Java,C#,Python等语言中,都可以使用try catch类似语法进行异常处理。C语言中没有异常机制,一般用返回值标识错误,判断返回值进行错误的处理。
#include <stdio.h> int divide(int a, int b, int* result) { if (b == 0) { return -1; // 返回错误代码表示除以零错误 } *result = a / b; return 0; // 成功返回0 } int main() { int result; int error = divide(10, 0, &result); if (error != 0) { printf("Error: Division by zero!\n"); } else { printf("Result: %d\n", result); } return 0; }
返回值表示出错的常见情况可能如下:
1)如果函数返回值是int
,并且返回的数据不可能是负数,直接返回-1
表示出错,非负数代表正常返回数据。
2)如果函数返回值是int
,但返回的数据也可能是负数;返回0
代表正常,-1
代表出错。用指针类型的参数带出返回的数据。
3)如果函数返回值是指针类型,返回NULL
(0)代表出错,其他代表正常。
4)如果函数不需要考虑出错情况,可以用void
返回值。
2、错误异常处理函数及全局变量
C标准中,对于错误处理提供了一个全局变量和3个函数:
1)全局变量
errno:外部的全局变量,用于储存错误的编号。errno
是一个标准库全局变量,用于表示最后一次系统调用的错误代码。C库函数在某些情况下会设置 errno
,以标识发生的错误。可以使用 <errno.h>
中定义的错误代码
2)异常错误处理函数
strerror()
,perror()
,printf()
都可以把错误的编号转换成错误的信息。
strerror():传入一个错误的编号,返回一个错误的信息
perror():打印当前的错误信息(自动查找errno得到的错误编号)
printf("%m"):打印当前错误信息(自动查找errno)
例如,
#include <stdio.h> #include <errno.h> #include <string.h> extern int errno ; int main () { FILE * pf; int errnum; pf = fopen ("cjavapy.txt", "rb"); if (pf == NULL) { errnum = errno; fprintf(stderr, "错误号: %d\n", errno); perror("通过 perror 输出错误"); fprintf(stderr, "打开文件错误: %s\n", strerror( errnum )); } else { fclose (pf); } return 0; }
3、程序退出状态值
通常情况下,程序成功执行完一个操作正常退出的时候会带有值 EXIT_SUCCESS
。在这里,EXIT_SUCCESS
是宏,它被定义为 0
。
如果程序中存在一种错误情况,当退出程序时,会带有状态值 EXIT_FAILURE
,被定义为 -1
。
例如,
#include <stdio.h> #include <stdlib.h> main() { int dividend = 20; int divisor = 5; int quotient; if( divisor == 0){ fprintf(stderr, "除数为 0 退出运行...\n"); exit(EXIT_FAILURE); } quotient = dividend / divisor; fprintf(stderr, "quotient 变量的值为: %d\n", quotient ); exit(EXIT_SUCCESS); }
4、日志记录
为增强调试和错误追踪,通常需要实现自定义的错误处理和日志记录机制。日志记录可以帮助我们在程序运行过程中追踪和记录错误信息。C语言中记录日志能帮助程序员在程序开发时调试、记录及监控数据。程序在正式运行时,也方便能过日志来排查问题。
1)简单的错误处理函数
可以实现一个统一的错误处理函数,打印错误信息并返回错误代码,如下,
#include <stdio.h> #include <stdlib.h> void handle_error(const char *message) { perror(message); // 输出错误信息,并根据 errno 的值打印系统级错误信息 exit(EXIT_FAILURE); // 终止程序 } int main() { FILE *file = fopen("non_existent_file.txt", "r"); if (file == NULL) { handle_error("File open failed"); } fclose(file); return 0; }
2)日志记录
日志系统可以用来记录运行时的状态、错误信息或调试信息。常见的方法是将日志输出到文件中。
#include <stdio.h> #include <time.h> #include <stdarg.h> // 简单的日志记录函数,将日志写入文件 void log_message(const char *format, ...) { FILE *logfile = fopen("program.log", "a"); if (logfile == NULL) { perror("Error opening log file"); return; } // 获取当前时间并写入日志文件 time_t now; time(&now); fprintf(logfile, "%s: ", ctime(&now)); // 写入时间戳 // 使用 va_list 来处理变长参数 va_list args; va_start(args, format); vfprintf(logfile, format, args); va_end(args); fprintf(logfile, "\n"); fclose(logfile); } int main() { FILE *file = fopen("non_existent_file.txt", "r"); if (file == NULL) { log_message("Error: Failed to open file '%s'", "non_existent_file.txt"); return -1; } fclose(file); log_message("File opened successfully."); return 0; }
3)结合 assert 进行调试
assert
是 C 标准库提供的一种调试辅助工具。它用于在开发过程中检查条件是否成立,如果条件不成立,程序会终止并显示错误信息。assert
通常用于开发和测试阶段,在发布版本中可以通过定义 NDEBUG
来禁用。
#include <stdio.h> #include <assert.h> int main() { int a = 5; int b = 0; assert(b != 0); // 如果 b == 0,程序终止并显示错误信息 printf("a / b = %d\n", a / b); return 0; }
4)完整日志记录实现
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdarg.h> enum {ERROR,WARNING,INFO,DEBUG}; //宏定义形式 #define LogRecord(_error_level,_fmt,_X...) \ do { \ time_t _current_time=time(NULL); \ char _file_name[256]="\0"; \ strftime(_file_name,sizeof(_file_name),"%Y-%m-%d_log_record.log",localtime(&_current_time)); \ FILE *_fp=NULL; \ _fp=fopen(_file_name,"a+"); \ if(_fp!=NULL) \ { \ char _time_str[32]; \ strftime(_time_str,sizeof(_time_str),"%Y-%m-%d %H:%M:%S",localtime(&_current_time)); \ if(_error_level==ERROR) \ { \ fprintf(_fp,"[%s]-[%s]-[%s]-[%d] :> "_fmt"\n",_time_str,"ERROR",__FILE__,__LINE__,##_X); \ } \ else if(_error_level==WARNING) \ { \ fprintf(_fp,"[%s]-[%s]-[%s]-[%d] :> "_fmt"\n",_time_str,"WARNING",__FILE__,__LINE__,##_X); \ } \ else if(_error_level==INFO) \ { \ fprintf(_fp,"[%s]-[%s]-[%s]-[%d] :> "_fmt"\n",_time_str,"INFO",__FILE__,__LINE__,##_X); \ } \ else if(_error_level==DEBUG) \ { \ fprintf(_fp,"[%s]-[%s]-[%s]-[%d] :> "_fmt"\n",_time_str,"DEBUG",__FILE__,__LINE__,##_X); \ } \ fclose(_fp); \ } \ }while(0); //函数形式 void log_reocrd(int error_level, const char *format, ...) { va_list args; FILE *fp=NULL; char time_str[32]; char file_name[256]; va_start (args, format); time_t time_log = time(NULL); strftime(file_name,sizeof(file_name),"%Y-%m-%d_log_history.log",localtime(&time_log)); if((fp=fopen(file_name,"a+"))!=NULL) { strftime(time_str,sizeof(time_str),"%Y-%m-%d %H:%M:%S",localtime(&time_log)); if(error_level==(int)ERROR) { fprintf (fp, "[%s]-[%s]-[%s]-[%d] :> ",time_str,"ERROR",__FILE__,__LINE__); vfprintf (fp,format,args); fprintf (fp,"\n"); } else if(error_level==(int)WARNING) { fprintf (fp, "[%s]-[%s]-[%s]-[%d] :> ",time_str,"WARNINGs",__FILE__,__LINE__); vfprintf (fp,format,args); fprintf (fp,"\n"); } else if(error_level==(int)INFO) { fprintf (fp, "[%s]-[%s]-[%s]-[%d] :> ",time_str,"INFO",__FILE__,__LINE__); vfprintf (fp,format,args); fprintf (fp,"\n"); } else if(error_level==(int)DEBUG) { fprintf (fp, "[%s]-[%s]-[%s]-[%d] :> ",time_str,"DEBUG",__FILE__,__LINE__); vfprintf (fp,format,args); fprintf (fp,"\n"); } fclose(fp); } va_end (args); } int main() { int i=10; double x=3.1415926; long long k=123144345; LogRecord(INFO,"i=%d",i) LogRecord(INFO,"x=%lf",x) LogRecord(INFO,"k=%lld",k) LogRecord(INFO,"cjavapy") LogRecord(INFO,"www.cjavapy.com",NULL) log_reocrd(INFO,"i=%d",i); log_reocrd(INFO,"x=%lf",x); log_reocrd(INFO,"k=%lld",k); log_reocrd(INFO,"cjavapy"); log_reocrd(INFO,"www.cjavapy.com",NULL); printf("%s","finish"); return 0; }