处理来自多个fread调用的错误的更简洁的方法

时间:2017-08-08 09:22:22

标签: c error-handling fread

我在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;
}

4 个答案:

答案 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_FIELDWRITE_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)

以下是对mallocfread操作进行分组的简单方法,只需检查一次是否正确完成:

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()是可以的。