【发布时间】:2020-06-08 21:31:37
【问题描述】:
众所周知,编写“完美”的错误处理代码在 C 语言中很难(而在其他语言中则非常困难)。 开发人员几乎总是忘记或丢弃的是在清理资源时处理错误。例如忽略 fclose 返回值是危险的。
无论如何,我尝试在一个小型 C89 程序上编写一个完美的错误处理代码,该程序将 in.txt 复制到 out.txt 中。我的目标是编写易于检查和维护的代码。添加新的“资源”或在中间添加可能失败的新函数调用应该很容易。必须尽可能避免代码重复。我的基本设计很简单:所有资源都必须初始化为“空”。如果出现错误,我只需跳转到“handle_error”。最后我总是叫“free_resources”。 “handle_error”和“free_resources”部分可以执行多次(例如,如果在释放资源时发生错误)而不会出现问题。
我的代码“完美”吗?
#include <stdio.h>
typedef int status;
#define SUCCESS 1
#define FAILURE 0
#define INPUT_FILE "in.txt"
#define OUTPUT_FILE "out.txt"
#define BUFFER_SIZE 2097152
char buffer[BUFFER_SIZE];
status copy_file()
{
FILE *input_file = NULL;
FILE *output_file = NULL;
size_t read_bytes;
size_t written_bytes;
status result;
input_file = fopen(INPUT_FILE, "rb");
if (!input_file) {
perror("Failed to open input file");
goto handle_error;
}
output_file = fopen(OUTPUT_FILE, "wb");
if (!output_file) {
perror("Failed to open output file");
goto handle_error;
}
while (1) {
read_bytes = fread(buffer, 1, sizeof(buffer), input_file);
if (read_bytes != sizeof(buffer) && ferror(input_file)) {
fprintf(stderr, "Failed to read from input file.\n");
goto handle_error;
}
written_bytes = fwrite(buffer, 1, read_bytes, output_file);
if (written_bytes != read_bytes) {
fprintf(stderr, "Failed to write to output file.\n");
goto handle_error;
}
if (read_bytes != sizeof(buffer))
break;
}
result = SUCCESS;
free_resources:
if (output_file) {
if (fclose(output_file)) {
output_file = NULL;
perror("Failed to close output file");
goto handle_error;
}
output_file = NULL;
}
if (input_file) {
if (fclose(input_file)) {
input_file = NULL;
perror("Failed to close input file");
goto handle_error;
}
input_file = NULL;
}
return result;
handle_error:
result = FAILURE;
goto free_resources;
}
int main()
{
return copy_file() ? 0 : 1;
}
【问题讨论】:
-
如果您有工作代码并且正在寻找评论,那么codereview.stackexchange.com 可能是一个更合适的地方。我的观点是,带有 goto 的代码和导致相同代码执行两次的流程并不完美。
-
更好的方法可能是编写包装函数,这样您就不必重复所有的错误检查。
-
每当打印有关文件操作错误的错误消息时,该错误消息不包括用于执行操作的路径,小猫就会死亡。 IOW,将“无法打开输入文件”替换为用于打开文件的路径。
-
没有。
SUCCESS和FAILURE应该是来自<stdlib.h>的EXIT_SUCCESS和EXIT_FAILURE。您不需要在免费资源代码中goto handle_error。如果您没有打开输入文件,您可能会认识到您将没有要关闭的输出文件。您应该认识到fread()可能返回少于缓冲区已满(但多于零)字节并且不处于错误状态 - 特别是如果文件名不是指磁盘文件。您应该在错误消息中报告文件名——这是perror()的一个特别弱点。 -
How to write perfect error handling我自己也想知道....
标签: c error-handling