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;
}