Valgrind 和滥用 new[]?

Valgrind and misuse of new[]?

Valgrind 在我的程序中抛出了一堆关于 new[] 运算符的错误。我将其浓缩为下面的一个较小的示例

#include <iostream>
#include <fstream>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdlib>
#include <omp.h>

int num_spline_pts = 90;

double *initialize_TriSol_a();
double *initialize_TriSol_b();

int main(int argc, char **argv) {
    double *a = initialize_TriSol_a(); 
    double *b = initialize_TriSol_b();

    return 0;
}

double *initialize_TriSol_a(){
    double *a = new double[num_spline_pts]; //Valgrind does not like
    double *b = new double[num_spline_pts]; //Valgrind does not like

    for (int i = 0; i < num_spline_pts; i++){
        a[i] = 1;
        b[i] = 4;
    }

    *a /= *b;
    for(int i=1;i<num_spline_pts-1;i++)
    {
        b[i] -= (a[i-1]);
        a[i] /=b[i]; 
    }

    return &a[0];
}

double *initialize_TriSol_b(){
    double *a = new double[num_spline_pts]; //Valgrind does not like
    double *b = new double[num_spline_pts]; //Valgrind does not like

    for (int i = 0; i < num_spline_pts; i++){
        a[i] = 1;
        b[i] = 4;
    }

    *a /= *b;
    for(int i=1;i<num_spline_pts-1;i++)
    {
        b[i] -= (a[i-1]);
        a[i] /=b[i]; 
    }
    b[num_spline_pts-1] -= a[num_spline_pts-2]; 
    return &b[0];
}

说明一下,在代码中我不得不反复求解一些三对角系统。但是,前向和后向扫描中使用的一些向量不会从一个矩阵更改为另一个矩阵,因此我在这里预先计算它们。该程序将编译,我可以根据需要在 main 中使用生成的向量 a、b。但是,valgrind returns 错误

==32039== 720 bytes in 1 blocks are definitely lost in loss record 2 of 5
==32039==    at 0x4A0674C: operator new[](unsigned long) (vg_replace_malloc.c:305)
==32039==    by 0x400727: initialize_TriSol_a() (test.cpp:22)
==32039==    by 0x4006F7: main (test.cpp:15)
==32039== 
==32039== 720 bytes in 1 blocks are definitely lost in loss record 3 of 5
==32039==    at 0x4A0674C: operator new[](unsigned long) (vg_replace_malloc.c:305)
==32039==    by 0x40073F: initialize_TriSol_a() (test.cpp:23)
==32039==    by 0x4006F7: main (test.cpp:15)
==32039== 
==32039== 720 bytes in 1 blocks are definitely lost in loss record 4 of 5
==32039==    at 0x4A0674C: operator new[](unsigned long) (vg_replace_malloc.c:305)
==32039==    by 0x40086C: initialize_TriSol_b() (test.cpp:41)
==32039==    by 0x400700: main (test.cpp:16)
==32039== 
==32039== 720 bytes in 1 blocks are definitely lost in loss record 5 of 5
==32039==    at 0x4A0674C: operator new[](unsigned long) (vg_replace_malloc.c:305)
==32039==    by 0x400884: initialize_TriSol_b() (test.cpp:42)
==32039==    by 0x400700: main (test.cpp:16)

这些指的是我在上面的代码中使用了新的 double[num_spline_pts] 运算符的地方。从尝试 google 周围寻找类似的问题,听起来问题可能是因为没有 delete[] 发生。但这些不是临时数组,我一次又一次地使用它们。我进一步感到困惑,因为在我的真实代码(不是这个测试示例)中,我以类似的方式使用 new[] else 并且 valgrind 不会抱怨它(即它是不断更新的数据,所以我永远不会结束使用 delete []).

我真的误用了 new[] 吗?或者 valgrind 只是在抱怨滥用的可能性?

编辑 好的,我清楚地看到我在 return a 和 b 的各自函数中删除 a 和 b 失败的原因。但是,在 main 中,我在整个程序中使用 a 和 b。我想我现在的问题是,删除创建的每个变量和数组的用法是否正确。即 main() 的末尾应该总是包含一堆删除吗?我只是觉得我没有在其他人的代码中看到过这一点。

错误指出 720 bytes in 1 blocks are definitely lost,不是误用 new[] 您没有调用 delete[] 并且存在内存泄漏,这就是 valgrind 抱怨的原因。

记住要始终释放内存(最好 - 在 C++11 中使用 std::unique_ptr,这样它就会发生 'automatically')。可能标准 std::vector<double> 才是您真正需要的。

您对 new[] 的使用是正确的。 Valgrind 抱怨的是内存泄漏,而不是无效的内存访问。

您需要匹配调用 delete[] 来解决这些问题。

我建议您改用 std::vectorstd::array。您还可以使用智能指针 std::unique_ptrstd::shared_ptr 来进行内存管理。

您的代码存在的问题是您未能解除分配您使用 new[] 分配的内容。为了解决这个问题,您需要在两个函数中添加 delete[] b,并在完成后对 main 中收到的指针调用 delete[]

double *initialize_TriSol_a(){
    double *a = new double[num_spline_pts];
    double *b = new double[num_spline_pts];
    ...
    delete[] b; // <<== Add this
    return a; // No need to use &a[0]
}
...
int main(int argc, char **argv) {
    double *a = initialize_TriSol_a(); 
    double *b = initialize_TriSol_b();
    ... // Do things with a and b
    delete[] a; // <<== Add this
    delete[] b; // <<== Add this
    return 0;
}

更好的方法是使用 vector<double>,因为它可以让您首先避免手动内存管理。

您需要 delete[] 完成阵列后。

例如:

double *initialize_TriSol_b(){
    double *a = new double[num_spline_pts]; //Valgrind does not like
    double *b = new double[num_spline_pts]; //Valgrind does not like

    for (int i = 0; i < num_spline_pts; i++){
        a[i] = 1;
        b[i] = 4;
    }

    *a /= *b;
    for(int i=1;i<num_spline_pts-1;i++)
    {
        b[i] -= (a[i-1]);
        a[i] /=b[i]; 
    }
    b[num_spline_pts-1] -= a[num_spline_pts-2]; 

    // what about a?
    return &b[0];
}

在该函数的末尾,您 return 指针 b (以一种迂回的方式)但是 a 超出范围而您没有 delete[]它。

这里:

int main(int argc, char **argv) {
    double *a = initialize_TriSol_a(); 
    double *b = initialize_TriSol_b();

    // what about delet[] a; ?
    // what about delet[] b; ?
    return 0;
}

你需要delete[]你的阵列,当你完成它们。

注意:

理想情况下,在现代 C++ 中,您应该避免使用 new and/or 进行原始分配,使用智能指针,这样您就不必调用 delete.

最好考虑将数据存储在 容器 中,例如 std::array or a std::vector

Valgrind 检测到内存泄漏。那就是你分配内存并且从不取消分配它的时候,这正是你在这里所做的。

使用向量。