处理来自多个 fread 调用的错误的更简洁的方法
Neater way to handle errors from multiple fread calls
我在 C 中有以下方法来加载二进制文件,看起来相当冗长乏味,必须检查每个 fread
调用的错误值,有没有更简洁的方法来处理这个问题?
我知道可以通过一次读取结构来减少一些调用,但是由于 C 可以在结构成员之间添加填充字节,我宁愿避免这种情况。
some_type_t *load_something(FILE *file) {
some_type_t *something = (some_type_t *)malloc(sizeof(some_type_t));
if (something == NULL) {
return NULL;
}
if (fread(&something->field1, sizeof(something->field1), 1, file) == 0) {
free(something);
return NULL;
}
if (fread(&something->field2, sizeof(something->field2), 1, file) == 0) {
free(something);
return NULL;
}
if (fread(&something->field3, sizeof(something->field3), 1, file) == 0) {
free(something);
return NULL;
}
uint16_t some_var1, some_var2, some_var3;
some_other_type_t *something_else1 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var1, sizeof(some_var1), 1, file) == 0) {
free(something);
free(something_else1);
return NULL;
}
some_other_type_t *something_else2 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var2, sizeof(some_var2), 1, file) == 0) {
free(something);
free(something_else1);
free(something_else2);
return NULL;
}
some_other_type_t *something_else3 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var3, sizeof(some_var3), 1, file) == 0) {
free(something);
free(something_else1);
free(something_else2);
free(something_else3);
return NULL;
}
// Do something with the vars and allocated something elses.
// ...
return something;
}
为什么不创建宏:
#define READ_FIELD(data) \
do { if (fread(&data, sizeof(data), 1, file) == 0) { \
free(something); \
free(something_else1);
free(something_else2);
return NULL; \
} } while(0)
然后像函数调用一样调用它:
READ_FIELD(something->field1);
READ_FIELD(something->field2);
READ_FIELD(some_var1);
READ_FIELD(some_var2);
代码将是相同的,但至少它现在是生成的而不是 copied/pasted(可能有错误)。
宏必须在所有可能的内存块上调用 free
,即使是那些尚未分配的内存块。唯一的限制是将未分配的设置为 NULL
,这样 free
就不会崩溃。并且要超级安全地更改为:
free(something); something = NULL;
(当然,如果 something
是分配指针的副本,设置为 NULL
并不能防止双重释放,它有限制)
您也可以将此技术应用于写入端,并且由于 M Oehm 建议,您可以在包装器宏中列出您想要 read/write 的内容:
#define DO_ALL \
DO_FIELD(something->field1); \
DO_FIELD(something->field2); \
DO_FIELD(some_var1); \
DO_FIELD(some_var2)
然后将 DO_FIELD
定义为 READ_FIELD
或 WRITE_FIELD
:
#define DO_FIELD READ_FIELD
DO_ALL;
#undef DO_FIELD
没有什么可以让您省去检查每个调用是否成功,但您可以使用 goto
改进代码结构(这实际上是 goto
在C, 伪代码如下):
if (first_call() < 0) goto error;
if (second_call() < 0) goto error;
// [...]
// when everything succeeded:
return result;
error:
// free resources
// return error-indicator, e.g.
return 0;
如果要释放的资源在您的函数运行过程中累积,请确保首先将它们全部初始化为 NULL
/0
(假设它们是指针)。然后,在 error
部分中对它们调用 free()
在它们尚未分配时无效。如果您使用自己的 "destructor functions",请确保以与 free()
相同的方式设计它们——当传递 NULL 值时,它应该是空操作。
您需要将所有 something
指针初始化为 NULL
并将清理集中在一个地方。
...
something = something_else1 = something_else2 = something_else3 = NULL;
...
some_other_type_t *something_else3 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var3, sizeof(some_var3), 1, file) == 0) {
goto error;
}
// Do something with the vars and allocated something elses.
// ...
return something;
error:
free(something);
free(something_else1);
free(something_else2);
free(something_else3);
return NULL;
}
释放一个 NULL
指针是可以的,它什么都不做,因此你不需要在调用 free
之前检查 someting
指针是否是 NULL
。
旁注:在 C 中,您不会转换 malloc
的 return 值。
这里有一个简单的方法来将 malloc
和 fread
操作分组并检查一次是否正确完成:
some_type_t *load_something(FILE *file) {
uint16_t some_var1, some_var2, some_var3;
some_type_t *something = malloc(sizeof(*something));
some_other_type_t *something_else1 = malloc(sizeof(*something_else1));
some_other_type_t *something_else2 = malloc(sizeof(*something_else2));
some_other_type_t *something_else3 = malloc(sizeof(*something_else3));
if (!something || !something_else1 || !something_else2 || !something_else3 ||
!fread(&something->field1, sizeof(something->field1), 1, file) ||
!fread(&something->field2, sizeof(something->field2), 1, file) ||
!fread(&something->field3, sizeof(something->field3), 1, file) ||
!fread(&some_var1, sizeof(some_var1), 1, file) ||
!fread(&some_var2, sizeof(some_var2), 1, file) ||
!fread(&some_var3, sizeof(some_var3), 1, file))
{
free(something);
free(something_else1);
free(something_else2);
free(something_else3);
return NULL;
}
// Do something with the vars and allocated something elses.
// ...
return something;
}
请注意,将空指针传递给 free()
是可以的。
我在 C 中有以下方法来加载二进制文件,看起来相当冗长乏味,必须检查每个 fread
调用的错误值,有没有更简洁的方法来处理这个问题?
我知道可以通过一次读取结构来减少一些调用,但是由于 C 可以在结构成员之间添加填充字节,我宁愿避免这种情况。
some_type_t *load_something(FILE *file) {
some_type_t *something = (some_type_t *)malloc(sizeof(some_type_t));
if (something == NULL) {
return NULL;
}
if (fread(&something->field1, sizeof(something->field1), 1, file) == 0) {
free(something);
return NULL;
}
if (fread(&something->field2, sizeof(something->field2), 1, file) == 0) {
free(something);
return NULL;
}
if (fread(&something->field3, sizeof(something->field3), 1, file) == 0) {
free(something);
return NULL;
}
uint16_t some_var1, some_var2, some_var3;
some_other_type_t *something_else1 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var1, sizeof(some_var1), 1, file) == 0) {
free(something);
free(something_else1);
return NULL;
}
some_other_type_t *something_else2 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var2, sizeof(some_var2), 1, file) == 0) {
free(something);
free(something_else1);
free(something_else2);
return NULL;
}
some_other_type_t *something_else3 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var3, sizeof(some_var3), 1, file) == 0) {
free(something);
free(something_else1);
free(something_else2);
free(something_else3);
return NULL;
}
// Do something with the vars and allocated something elses.
// ...
return something;
}
为什么不创建宏:
#define READ_FIELD(data) \
do { if (fread(&data, sizeof(data), 1, file) == 0) { \
free(something); \
free(something_else1);
free(something_else2);
return NULL; \
} } while(0)
然后像函数调用一样调用它:
READ_FIELD(something->field1);
READ_FIELD(something->field2);
READ_FIELD(some_var1);
READ_FIELD(some_var2);
代码将是相同的,但至少它现在是生成的而不是 copied/pasted(可能有错误)。
宏必须在所有可能的内存块上调用 free
,即使是那些尚未分配的内存块。唯一的限制是将未分配的设置为 NULL
,这样 free
就不会崩溃。并且要超级安全地更改为:
free(something); something = NULL;
(当然,如果 something
是分配指针的副本,设置为 NULL
并不能防止双重释放,它有限制)
您也可以将此技术应用于写入端,并且由于 M Oehm 建议,您可以在包装器宏中列出您想要 read/write 的内容:
#define DO_ALL \
DO_FIELD(something->field1); \
DO_FIELD(something->field2); \
DO_FIELD(some_var1); \
DO_FIELD(some_var2)
然后将 DO_FIELD
定义为 READ_FIELD
或 WRITE_FIELD
:
#define DO_FIELD READ_FIELD
DO_ALL;
#undef DO_FIELD
没有什么可以让您省去检查每个调用是否成功,但您可以使用 goto
改进代码结构(这实际上是 goto
在C, 伪代码如下):
if (first_call() < 0) goto error;
if (second_call() < 0) goto error;
// [...]
// when everything succeeded:
return result;
error:
// free resources
// return error-indicator, e.g.
return 0;
如果要释放的资源在您的函数运行过程中累积,请确保首先将它们全部初始化为 NULL
/0
(假设它们是指针)。然后,在 error
部分中对它们调用 free()
在它们尚未分配时无效。如果您使用自己的 "destructor functions",请确保以与 free()
相同的方式设计它们——当传递 NULL 值时,它应该是空操作。
您需要将所有 something
指针初始化为 NULL
并将清理集中在一个地方。
...
something = something_else1 = something_else2 = something_else3 = NULL;
...
some_other_type_t *something_else3 = (some_other_type_t *)malloc(sizeof(some_other_type_t));
if (fread(&some_var3, sizeof(some_var3), 1, file) == 0) {
goto error;
}
// Do something with the vars and allocated something elses.
// ...
return something;
error:
free(something);
free(something_else1);
free(something_else2);
free(something_else3);
return NULL;
}
释放一个 NULL
指针是可以的,它什么都不做,因此你不需要在调用 free
之前检查 someting
指针是否是 NULL
。
旁注:在 C 中,您不会转换 malloc
的 return 值。
这里有一个简单的方法来将 malloc
和 fread
操作分组并检查一次是否正确完成:
some_type_t *load_something(FILE *file) {
uint16_t some_var1, some_var2, some_var3;
some_type_t *something = malloc(sizeof(*something));
some_other_type_t *something_else1 = malloc(sizeof(*something_else1));
some_other_type_t *something_else2 = malloc(sizeof(*something_else2));
some_other_type_t *something_else3 = malloc(sizeof(*something_else3));
if (!something || !something_else1 || !something_else2 || !something_else3 ||
!fread(&something->field1, sizeof(something->field1), 1, file) ||
!fread(&something->field2, sizeof(something->field2), 1, file) ||
!fread(&something->field3, sizeof(something->field3), 1, file) ||
!fread(&some_var1, sizeof(some_var1), 1, file) ||
!fread(&some_var2, sizeof(some_var2), 1, file) ||
!fread(&some_var3, sizeof(some_var3), 1, file))
{
free(something);
free(something_else1);
free(something_else2);
free(something_else3);
return NULL;
}
// Do something with the vars and allocated something elses.
// ...
return something;
}
请注意,将空指针传递给 free()
是可以的。