【问题标题】:Is there way to verify my program has no memory leaks?有没有办法验证我的程序没有内存泄漏?
【发布时间】:2020-09-29 20:21:31
【问题描述】:

我希望确定以下程序(查找最大子数组的实现)是否泄漏内存。有没有通用的方法来确定这一点?例如使用调试器的某些功能?什么是一般策略?

struct Interval {
   int max_left;
   int max_right;
   int sum;
};

struct Interval * max_crossing_subarray(int A[], int low, int mid, int high) {
    struct Interval * crossing = malloc(sizeof(struct Interval));

    int left_sum = INT_MIN;
    int sum = 0;

    for(int i = mid; i >= low; --i) {
        sum = sum + A[i];
        if(sum > left_sum) {
            left_sum = sum;
            crossing->max_left = i;
        }
    }

    int right_sum = INT_MIN;
    sum = 0;

    for(int j = mid+1; j <= high; ++j) {
        sum = sum + A[j];
        if(sum > right_sum) {
            right_sum = sum;
            crossing->max_right = j;
        }
    }

    crossing->sum = left_sum + right_sum;

    return crossing;
}

struct Interval * max_subarray(int A[], int low, int high) {
    if(high == low) {
        struct Interval * base = malloc(sizeof(struct Interval));
        *base = (struct Interval) { low, high, A[low] };
        return base;
    } else {
        int mid = floor((low+high)/2);
        struct Interval * left = malloc(sizeof(struct Interval));
        struct Interval * right = malloc(sizeof(struct Interval));
        left = max_subarray(A, low, mid);
        right = max_subarray(A, mid+1, high);
        struct Interval * cross = max_crossing_subarray(A, low, mid, high);
        if(left->sum >= right->sum & right->sum >= cross->sum) {
            free(right);
            free(cross);
            return left;
        } else if(right->sum >= left->sum & right->sum >= cross-> sum) {
            free(left);
            free(cross);
            return right;
        } else {
            free(left);
            free(right);
            return cross;
        }
    }
}

int main()
{
    int A[] = {-10, 7, -5, -3, 40, 4, -1, 8, -3, -1, -5, 20, 7};
    struct Interval * result = max_subarray(A, 0, 12);

    printf("left: %i, right: %i, sum: %i\n", result->max_left, result->max_right, result->sum);

    return 0;
}

由于程序的递归性质,很难理解(至少对我而言)。我想我已经完成了所有工作,但我想找到一种方法来确定。

编辑所选答案中建议的软件允许我找到我所有的漏洞,正如评论中指出的那样,没有理由分配左右,下面是内存无泄漏代码。

struct Interval {
   int max_left;
   int max_right;
   int sum;
};

struct Interval * max_crossing_subarray(int A[], int low, int mid, int high) {
    struct Interval * crossing = malloc(sizeof(struct Interval));

    int left_sum = INT_MIN;
    int sum = 0;

    for(int i = mid; i >= low; --i) {
        sum = sum + A[i];
        if(sum > left_sum) {
            left_sum = sum;
            crossing->max_left = i;
        }
    }

    int right_sum = INT_MIN;
    sum = 0;

    for(int j = mid+1; j <= high; ++j) {
        sum = sum + A[j];
        if(sum > right_sum) {
            right_sum = sum;
            crossing->max_right = j;
        }
    }

    crossing->sum = left_sum + right_sum;

    return crossing;
}

struct Interval * max_subarray(int A[], int low, int high) {
    if(high == low) {
        struct Interval * base = malloc(sizeof(struct Interval));
        *base = (struct Interval) { low, high, A[low] };
        return base;
    } else {
        int mid = floor((low+high)/2);
        struct Interval * left = max_subarray(A, low, mid);
        struct Interval * right = max_subarray(A, mid+1, high);
        struct Interval * cross = max_crossing_subarray(A, low, mid, high);
        if(left->sum >= right->sum & right->sum >= cross->sum) {
            free(right);
            free(cross);
            return left;
        } else if(right->sum >= left->sum & right->sum >= cross-> sum) {
            free(left);
            free(cross);
            return right;
        } else {
            free(left);
            free(right);
            return cross;
        }
    }
}

int main()
{
    int A[] = {-10, 7, -5, -3, 40, 4, -1, 8, -3, -1, -5, 20, 7};
    struct Interval * result = max_subarray(A, 0, 13-1);

    printf("left: %i, right: %i, sum: %i\n", result->max_left, result->max_right, result->sum);

    return 0;
}

【问题讨论】:

  • 在我看来,它几乎泄漏了它分配的所有内容。您为leftright 分配,然后立即通过将其他内容分配给变量来丢弃分配。所以记忆丢失了,然后不知道frees实际上是免费的。
  • Ivor Denham-Dyson,很好奇,为什么是 floor((low+high)/2); 而不是 (low+high)/2;
  • @chux-ReinstateMonica (low+high)/2 不保证返回整数,尽管我认为隐式转换不会导致问题。
  • IvorDenham-Dyson,感谢您提供的信息。对于int lowint high(low+high)/2 返回一个int。没有演员表。商的分数被丢弃 - 向零舍入。
  • @chux-ReinstateMonica 同意,我在翻译伪代码时已将其包含在内,但是的,我认为它是不必要的。

标签: c debugging memory memory-management memory-leaks


【解决方案1】:

您可以使用valgrind。它是适用于 Linux 和其他类 UNIX 系统的内存调试工具,可以发现内存泄漏以及无效的内存访问。

当我通过 valgrind 运行这段代码时,它会输出以下内容:

[dbush@db-centos7 ~]$ valgrind ./x1
==3406== Memcheck, a memory error detector
==3406== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3406== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==3406== Command: ./x1
==3406== 
left: 4, right: 12, sum: 69
==3406== 
==3406== HEAP SUMMARY:
==3406==     in use at exit: 300 bytes in 25 blocks
==3406==   total heap usage: 49 allocs, 24 frees, 588 bytes allocated
==3406== 
==3406== LEAK SUMMARY:
==3406==    definitely lost: 300 bytes in 25 blocks
==3406==    indirectly lost: 0 bytes in 0 blocks
==3406==      possibly lost: 0 bytes in 0 blocks
==3406==    still reachable: 0 bytes in 0 blocks
==3406==         suppressed: 0 bytes in 0 blocks
==3406== Rerun with --leak-check=full to see details of leaked memory
==3406== 
==3406== For counts of detected and suppressed errors, rerun with: -v
==3406== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

所以你有一些泄漏。现在让我们通过 --leak-check=full 选项来查看这些泄漏的确切位置:

==11531== Memcheck, a memory error detector
==11531== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11531== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==11531== Command: ./x1
==11531== 
left: 4, right: 12, sum: 69
==11531== 
==11531== HEAP SUMMARY:
==11531==     in use at exit: 300 bytes in 25 blocks
==11531==   total heap usage: 49 allocs, 24 frees, 588 bytes allocated
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 1 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 2 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 3 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 4 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 5 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 6 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 7 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 8 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 9 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 10 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 11 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 12 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 13 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 14 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 15 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 16 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 17 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 18 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 19 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 20 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 21 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 22 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 23 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007A8: max_subarray (x1.c:49)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 24 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x4007B6: max_subarray (x1.c:50)
==11531==    by 0x4007CE: max_subarray (x1.c:51)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x4007E9: max_subarray (x1.c:52)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== 12 bytes in 1 blocks are definitely lost in loss record 25 of 25
==11531==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==11531==    by 0x40065B: max_crossing_subarray (x1.c:13)
==11531==    by 0x400802: max_subarray (x1.c:53)
==11531==    by 0x400931: main (x1.c:73)
==11531== 
==11531== LEAK SUMMARY:
==11531==    definitely lost: 300 bytes in 25 blocks
==11531==    indirectly lost: 0 bytes in 0 blocks
==11531==      possibly lost: 0 bytes in 0 blocks
==11531==    still reachable: 0 bytes in 0 blocks
==11531==         suppressed: 0 bytes in 0 blocks
==11531== 
==11531== For counts of detected and suppressed errors, rerun with: -v
==11531== ERROR SUMMARY: 25 errors from 25 contexts (suppressed: 0 from 0)

这些泄漏大多来自以下两行:

    struct Interval * left = malloc(sizeof(struct Interval));
    struct Interval * right = malloc(sizeof(struct Interval));

如果我们看看接下来的两行,原因就很明显了:

    left = max_subarray(A, low, mid);
    right = max_subarray(A, mid+1, high);

因此,在将已分配内存的地址分配给这些指针之后,您会用其他值覆盖这些地址,从而导致泄漏。这可以通过摆脱 malloc 调用并使用函数调用的结果进行初始化来解决:

    struct Interval * left = max_subarray(A, low, mid);
    struct Interval * right = max_subarray(A, mid+1, high);

最后一个在max_crossing_subarray

struct Interval * crossing = malloc(sizeof(struct Interval));

这个指针是函数返回的,所以我们需要看看丢失的free在哪里。环顾一番后,我们看到它是从max_subarray 调用的,它最终将它返回​​给main 作为result

struct Interval * result = max_subarray(A, 0, 13-1);

printf("left: %i, right: %i, sum: %i\n", result->max_left, result->max_right, result->sum);

return 0;

但是正如你所看到的,这里没有调用free,所以让我们添加它:

struct Interval * result = max_subarray(A, 0, 13-1);

printf("left: %i, right: %i, sum: %i\n", result->max_left, result->max_right, result->sum);

free(result);
return 0;

现在完成这些修复后,我们将再次运行 valgrind:

==11736== Memcheck, a memory error detector
==11736== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11736== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==11736== Command: ./x1
==11736== 
left: 4, right: 12, sum: 69
==11736== 
==11736== HEAP SUMMARY:
==11736==     in use at exit: 0 bytes in 0 blocks
==11736==   total heap usage: 25 allocs, 25 frees, 300 bytes allocated
==11736== 
==11736== All heap blocks were freed -- no leaks are possible
==11736== 
==11736== For counts of detected and suppressed errors, rerun with: -v
==11736== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

并且泄漏消失了。

【讨论】:

  • 这太有用了。谢谢
  • Valgrind 不仅适用于 Linux。它也适用于 Solaris、Android 和大多数 macOS。
【解决方案2】:

一般来说,除非您将语言限制为功能较少的子语言(如 misra),否则您无法证明程序的正确性。一般来说,这个问题是无法确定的。

但是您可以使用 lint 等软件对数学模式进行静态检查,或使用 valgrind 进行动态检查,或者使用 Coq 等语言,其中程序是证明,它们使用 Hoare 逻辑来对您的代码进行陈述。例如,用Hoare逻辑证明了Windows内核永远不会出现段错误。

【讨论】:

    【解决方案3】:

    除了已经提到的检测器,包括最突出的valgrind,您还可以使用AddressSanitizer 工具,该工具集成了LeakSanitizer,并在GCC 自版本4.8 和Clang 自版本3.1 以来实现。

    各自的编译器标志是-fsanitize=address-fsanitize=leak

    此外,您可以使用MemorySanitizer 来尝试读取未初始化的数据。


    对于gcc,您可以找到所有相关标志here

    https://clang.llvm.org/docs/AddressSanitizer.html

    How to use AddressSanitizer with GCC?

    【讨论】:

      【解决方案4】:

      在程序的所有情况下,由 malloc 结构分配的每个都必须由 free 释放。 因此,在所有情况下,您都会返回一个 Interval 实例。您必须在主块中释放它。 或者您可以使用智能指针/分配器。 您还可以为 Interval 实现 operator =,并使用实例而不是指针。 为了快速分配返回值,您可以使用 std::swap

      Intreval & operator=(Intreval && a)
      {
          std::swap(a.max_left,max_left);
          std::swap(a.max_rignt,max_rignt);
          std::swap(a.sum,sum);
          return *this;
      }
      

      最后,如果你不是 shure,就不要使用 malloc。

      【讨论】:

      • 感谢 Anton,这个问题是关于 C 而不是 C++,但也许有人会觉得它有帮助。
      • 在这种情况下,只有主块中的 free 可以提供帮助。 :-) 我没注意标签“c”,抱歉。
      猜你喜欢
      • 1970-01-01
      • 2013-04-30
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      • 2019-06-25
      • 1970-01-01
      • 2012-02-25
      相关资源
      最近更新 更多