无论 return 代码如何,都在循环中释放内存

Free memory in a loop regardless of return code

我有以下 C 伪代码:

int main()
{
    int rc = 0;

    for (int i = 1; i < 10; i++) {
        char *data = malloc(i); // Each iteration has a different alloc size

        rc = do_something(data);
        if (rc != 0) goto cleanup;

        rc = do_something_else();
        if (rc != 0) goto cleanup;

cleanup:
        free(data);
        if (rc != 0) break;
    }
    return rc;
}

我想模拟 Python 的 try...finally 模式,方法是在调用的函数 returns 出现错误时跳出循环,但前提是需要进行一些必要的清理工作。

到目前为止,代码对我来说还不错,但我是 C 的新手。是否有不同的模式可以避免在 free 之后重复进行 rc != 0 测试? (我知道有些人认为 goto 绝对错误,但我认为这是我为这种情况找到的最干净的解决方案。)

根据我的经验,在 C 中很少有真正简单的解决此类错误处理问题的方法。

你可以这样做,但是否真的更好还有待商榷。

int main()
{
    int rc = 0;

    for (int i = 1; i < 10; i++) {
        char *data = malloc(i); // Each iteration has a different alloc size

        rc = do_something(data);
        if (rc != 0) goto cleanup;

        rc = do_something_else();
        if (rc != 0) goto cleanup;
        
        free(data);
        continue;

cleanup:
        free(data);
        break;
    }
    return rc;
}

在需要资源管理并且需要执行一组检查而每次失败都需要停止流程并执行所需清理的情况下,可以使用这种模式(使用 goto) .

在我看来,要使代码更简洁,可以将 rc != 0 条件放在 for 循环中:

int rc = 1;
for (int i = 0; i < 10 && rc != 0; i++)
{
    ...

在这种情况下,您根本不需要 break,因为一旦 rc 与 0 进行比较,循环就会停止。我还建议检查 [=17= 的结果]:

int foo(void)
{
    int rc = 1;

    for (int i = 1; i < 10 && rc != 0; i++) {
        char *data = malloc(i); // Each iteration has a different alloc size
        if (!(rc = !!data)) goto cleanup;

        rc = do_something(data);
        if (rc != 0) goto cleanup;

        rc = do_something_else();
        if (rc != 0) goto cleanup;

cleanup:
        free(data);
    }

    return rc;
}

注意free一个NULL指针是完全合法的。

在您展示的特定 code/case 中,您可以使用 realloc 函数而不是 malloc 来消除对任何类型的每次循环清理的需要。这将简单地用一个新块(不同大小)替换在前一个循环(如果有的话)上分配的内存。然后,您可以简单地将任何清理(即调用 free)推迟到循环外。

您仍然可以在发生错误时使用break语句退出循环;或者,如评论中所述,您可以向循环条件添加 rc != 0 测试。

这里有一些 C 代码可以按照我的指示执行(但是,当然,您需要两个被调用函数的实际 定义 才能工作):

#include <stdlib.h>
int do_something(char* data);
int do_something_else(void);

int main()
{
    int rc = 0;
    char* data = NULL;
    for (size_t i = 1; i < 10; i++) {
        char* temp = realloc(data, i); // If "data" is NULL (1st loop) -> same as malloc
        if (temp == NULL) { // Allocation failure...
            // Error message
            break;
        }
        data = temp; // Succesfull allocation, so update "data" pointer

        rc = do_something(data);
        if (rc != 0) break;

        rc = do_something_else();
    }
    free(data); // We now only have to do the cleanup once (calling with NULL is allowed).
    return rc;
}

随时要求进一步澄清and/or解释。

您可以在循环外声明变量。然后只要你释放它就让变量为空,并在循环后检查它。

    char *data = NULL;
    for (int i = 1; i < 10; i++) {
        data = malloc(i); // Each iteration has a different alloc size

        rc = do_something(data);
        if (rc != 0) break;

        rc = do_something_else();
        if (rc != 0) break;

        free(data);
        data = NULL;
    }
    if (data) {
        free(data);
    }