我在C中有以下方法来加载二进制文件,从每次fread
调用中检查错误值似乎相当漫长而乏味,是否有更简洁的方法来处理它?</ p>
我知道有些调用可以通过一次性读取结构来减少,但是由于C可以在struct成员之间添加填充字节,我宁愿避免这种情况。
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;
}
答案 0 :(得分:2)
为什么不创建宏:
#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);
代码将是相同的,但至少它现在已生成,未被复制/粘贴(可能有错误)。
宏必须在所有可能的内存块上调用free
,即使是那些尚未分配的内存块。唯一的限制是将未分配的约束设置为NULL
,以便free
不会崩溃。并以超级安全的方式改变:
free(something); something = NULL;
(当然如果something
是分配指针的副本,设置为NULL
并不能防止双重释放,它有限制)
你也可以将这种技术应用到写入端,并且由于M Oehm建议,你可以在包装器宏中列出你想要读/写的内容:
#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
答案 1 :(得分:2)
没有任何东西可以帮助您检查每次调用是否成功,但您可以使用goto
改进代码结构(这实际上是在C语言中使用goto
的惯用语法,要遵循的伪代码:
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
(假设它们是指针)。然后,在free()
部分中调用error
时,如果尚未分配它们,则无效。如果使用自己的“析构函数”,请确保以free()
设计的方式设计它们 - 当传递NULL值时,它应该是无操作。
答案 2 :(得分:1)
您需要初始化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
指针即可,它什么都不做,因此在调用someting
之前,您不需要检查NULL
指针是否为free
。
旁注:在C语言中,您不会投放malloc
的返回值。
答案 3 :(得分:0)
以下是对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()
是可以的。