#为什么需要错误处理
大多数程序员假定错误不会发生,并且这一乐观的思想影响了他们所用和创造的语言。
C通过返回错误码或设置全局的
errno
值来解决这些问题,并且你需要检查这些值。
通过这种机制检查现存的复杂代码中,你所执行的东西是否发生错误。模式如下
- 调用函数
- 如果返回值出现错误(每次必须检查)
- 清理创建的所有资源
- 打印出所有可能有帮助的错误信息
接下来将专注于这些步骤的实现
实现错误机制意味着每个函数调用结束后都需要3~4行额外代码,太麻烦。使用一系列“调试宏”作为解决方案。
几个重要的认知:
- 错误检查可以在代码里直接写出,不过通常需要3~4行
- 利用宏(而不是函数)来实现是由许多优势的
- 简洁
- 错误处理时需要用到
file:line
信息,如果在函数内部执行这些会得到错误信息 - goto函数的实现会相当麻烦
- goto 可以和标签结合使用
- 宏的使用也有相当多的技巧,与函数有异曲同工之妙,但在使用方式上更加灵活(某些方面)
宏定义
#ifndef __dbg_h__
#define __dbg_h__#include <stdio.h>
#include <errno.h> //引入errno变量
#include <string.h>#ifdef NDEBUG //可以通过宏定义来消除所有调试信息
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)//输出错误信息,文件名,行号等。其中:##__VA_ARGS__表示宏中... 可以类似调用printf使用debug
#endif#define clean_errno() (errno == 0 ? "None" : strerror(errno))#define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)#define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)#define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }#define check_mem(A) check((A), "Out of memory.")#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; }#endif
代码解释
<errno.h>头文件
errno不是一个库,而是一个全局变量。
当调用一些系统库函数时,他们可能会设置errno
的值来指示错误的类型。
通常,成功完成函数调用时会置零。
使用实例
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main() {FILE *file = fopen("nonexistent_file.txt", "r");if (file == NULL) {perror("Error opening file"); //自动根据error输出错误信息,终端显示:Error opening file: No such file or directoryfprintf(stderr, "errno = %d\n", errno); //输出到标准输出流,终端显示:error = xexit(EXIT_FAILURE);}// Use the file...fclose(file);return 0;
}
综上理解:errno是一个全局变量,可以用来获取函数执行失败后的错误信息代号。
通常搭配perror
或fprintf(stderr, " xxx%dxxx " , errno)
使用
debug宏
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
输出错误信息到终端:__FILE__
代表文件名,__LINE__
代表行号,##__VA_ARGS__
代表可变参数部分...
这里对于debug的使用类似于调用printf
- M是想要输出的错误字符串,注意不带换行符。
- 后续可变参数可以替换M 中格式化预留的内容。
实例:
debug("I am %d years old.", 37);
终端输出
DRBUG ex20.c:11: I am 37 years old