在C语言中,错误处理一直是开发者的任务。标准库中没有异常,错误通过返回码或全局变量errno返回(开始使用于1970年代的UNIX,后续为POSIX和ANSI C)。这些机制今天仍用于在异常情况下控制执行流。
在使用标准函数(文件操作、内存分配、字符串函数)时,错误可能在没有特别控制的情况下不易察觉。错误处理不当——忽略返回码、不正确地解释errno、缺乏资源清理——会导致程序运行不正确、崩溃和安全漏洞。
正确的错误处理要求必须分析函数返回的值,使用errno仅在失败后,及提供信息丰富的错误输出。返回码适用于内部函数——允许无全球副作用的处理。errno更常用于系统调用和标准库函数。每次潜在危险的操作后,都要分析返回结果,而全局状态(errno)不应被中间调用覆盖。
代码示例:
#include <stdio.h> #include <errno.h> #include <string.h> FILE *open_file(const char *filename) { errno = 0; FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "错误: %s ", strerror(errno)); } return f; }
关键特点:
可以将errno用于用户定义函数,以便向上报告错误吗?
不可以,errno仅用于标准库和系统调用。它是全局的,可以在任何地方被重写,不适合自己的函数。
在每次调用函数之前必须设置errno吗?
不必,但建议在调用之前重置errno(例如,设置为零),如果计划分析更改的话。并不是每个函数在成功时都会改变errno,仅在错误时才会。
errno = 0; ... 调用危险函数 ...
可以在任何函数之后信任errno吗?
仅适用于那些根据标准在失败时明确设置errno的函数。许多标准库函数在成功时不会触碰errno。文档是你的朋友。
打开文件时不检查结果,错误没有被分析,程序在缺少文件时运行不正确:
优点:
缺点:
在每个关键函数后检查结果,如出错则输出详细信息,结合strerror(errno),程序正确终止:
优点:
缺点: