在函数中使用 delete[]:指定给 RtlValidateHeap 的地址无效
Using delete[] in a function: Invalid address specified to RtlValidateHeap
我正在编写一个程序来求解随时间变化的磁场的 Bloch 方程。我的磁场值变化范围太大,无法使用简单的 Runge Kutta 方法,但我有一个标准的 4 阶,其步长大小设置为与恒定的均匀磁场一起工作。现在我正在尝试编写一个程序,该程序使用嵌入式 4-5 阶方法来使用截断错误,并调整我的步长(C 中的数值食谱(第 2 版)中的方法)
我的函数是这样的:
void rungekuttaKC(double a[6], double b[6][6], double c[6], double cs[6], double h, double gammaN, double bx, double by, double bz, double *mu) {
double *mu5 = new double[3];
mu5 = mu;
double *mu4 = new double[4];
mu4 = mu;
double *kx = new double[6];
double *ky = new double[6];
double *kz = new double[6];
double add[3] = { 0.0, 0.0, 0.0 };
double addmu[3] = { 0.0, 0.0, 0.0 };
double addmus[3] = { 0.0, 0.0, 0.0 };
for (int i = 0; i < 6; i++) {
for (int j = 0; j < i; j++) {
add[0] = add[0] + kx[j] * b[i][j];
add[1] = add[1] + ky[j] * b[i][j];
add[2] = add[2] + kz[j] * b[i][j];
}
kx[i] = h*gammaN*((mu[1] + h*add[1]) * bz - (mu[2] + h*add[2]) * by);
ky[i] = h*gammaN*((mu[2] + h*add[2]) * bx - (mu[0] + h*add[0]) * bz);
kz[i] = h*gammaN*((mu[0] + h*add[0]) * by - (mu[1] + h*add[1]) * bx);
}
for (int l = 0;l < 6;l++){
addmu[0] = addmu[0] + kx[l] * c[l];
addmu[1] = addmu[1] + ky[l] * c[l];
addmu[2] = addmu[2] + kz[l] * c[l];
addmus[0] = addmu[0] + kx[l] * cs[l];
addmus[1] = addmu[1] + ky[l] * cs[l];
addmus[2] = addmu[2] + kz[l] * cs[l];
}
for (int m = 0;m <=2; m++) {
mu4[m] = mu4[m] + addmus[m];
mu5[m] = mu5[m] + addmu[m];
mu[m] = mu4[m];
}
//delete[] mu5;
//delete[] mu4;
//delete[] kx;
//delete[] ky;
//delete[] kz;
//delete[] add;
//delete[] addmu;
//delete[] addmus;
}
a、b、c、cs为Cash-Karp法Bucher表中的值,h为步长,1.0e-8。
static double a[6] = { 1.0 / 5.0, 3.0 / 10.0, 3.0 / 5.0, 1.0, 7.0 / 8.0 };
static double bij[6][6] = { {0.0,0.0,0.0,0.0,0.0,0.0},
{1.0 / 5.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{3.0 / 40.0, 9.0/40.0, 0.0, 0.0, 0.0, 0.0},
{3.0/10.0, -9.0/10.0, 6.0/5.0, 0.0, 0.0, 0.0},
{-11.0/54.0, 5.0/2.0, -70.0/27.0, 35.0/27.0, 0.0},
{1631.0/55296.0, 175.0/512.0, 575.0/13824.0, 44275.0/110592.0, 253.0/4096.0} };
static double c[6] = { 37.0 / 378.0, 0.0, 250.0/621.0, 125.0/594.0, 0.0, 512.0/1771.0 };
static double cs[6] = {2825.0/27648.0, 0.0, 18575.0/48384.0, 13525.0/55296.0, 277.0/14336.0, 1.0/4.0 };
磁场值由插值器在函数外部设置并传入,但只是为了确保它产生振荡,我用恒定磁场对其进行测试,其中 bx 和 by 为 0,bz是5。传入的mu开始为{1,0,0}
现在,当我在函数末尾不使用任何删除命令时,整个程序都在调试器中运行,但是存在非常明显的内存泄漏。
当我只对用 new 初始化的数组使用 delete 时,弹出 window 错误 "Main.exe has triggered a breakpoint." 并且没有其他信息。在输出 window 我得到
“ HEAP[Main.exe]:指定给 RtlValidateHeap 的地址无效(00470000,0016F514)
Main.exe 已触发断点。"
当我在函数末尾删除所有这些时,我得到了同样的错误,但 RtlValidateHeap 中的地址不同。
我在发布模式下尝试 运行,并切换我的运行时库,我得到“检测到严重错误 c0000374
Main.exe 已触发断点。"
我是在误用 delete[] 还是我做错了什么?这个错误是什么意思?
double *mu5 = new double[3];
mu5 = mu;
这不会复制数组,这会重新分配指针,然后您尝试删除错误的数组(即作为函数参数的数组而不是您分配的数组)。
这是 C++(和 C)中原始数组的众多错误之一,请使用容器(在本例中为 std::vector<T>
或 std::array<T>
),如果可以的话它们已经过载 operator=
做你期望的事。
也就是说:你为什么要使用动态分配? A) 你知道前面数组的大小,B) 它们不够大以至于溢出堆栈。
您正在删除 add
、addmu
和 addmus
。这些是在堆栈上分配的(而不是在 new
的堆上)。因此,他们不能 delete[]
'。
由于您在编译时知道数组的所有大小并且它们很小,因此您可能应该将它们全部分配到堆栈上。对于动态数组,在 C++ 中,最佳做法是使用像 vector
这样的容器,这使您免于手动使用 delete
的责任。
此外,正如 Borgleader 所说,行
double *mu5 = new double[3];
mu5 = mu;
double *mu4 = new double[4];
mu4 = mu;
没有按照您的期望去做。首先在堆上分配一个double[3]
,它的地址给mu5
。然后,您擦除该地址并用 mu
覆盖它。当您 delete[] mu5
时,您正在释放 mu
指向的内存(这可能不是故意的),而 new double[3]
仍然存在。现在要通过最少的更改更正它,您可以将该代码段替换为
double *mu5 = new double[3];
std::copy(mu, mu + 3, mu5);
double *mu4 = new double[4];
std::copy(mu, mu + 4, mu4);
但是,写这个是C风格,不是C++风格(虽然它使用C++语法)。
我正在编写一个程序来求解随时间变化的磁场的 Bloch 方程。我的磁场值变化范围太大,无法使用简单的 Runge Kutta 方法,但我有一个标准的 4 阶,其步长大小设置为与恒定的均匀磁场一起工作。现在我正在尝试编写一个程序,该程序使用嵌入式 4-5 阶方法来使用截断错误,并调整我的步长(C 中的数值食谱(第 2 版)中的方法)
我的函数是这样的:
void rungekuttaKC(double a[6], double b[6][6], double c[6], double cs[6], double h, double gammaN, double bx, double by, double bz, double *mu) {
double *mu5 = new double[3];
mu5 = mu;
double *mu4 = new double[4];
mu4 = mu;
double *kx = new double[6];
double *ky = new double[6];
double *kz = new double[6];
double add[3] = { 0.0, 0.0, 0.0 };
double addmu[3] = { 0.0, 0.0, 0.0 };
double addmus[3] = { 0.0, 0.0, 0.0 };
for (int i = 0; i < 6; i++) {
for (int j = 0; j < i; j++) {
add[0] = add[0] + kx[j] * b[i][j];
add[1] = add[1] + ky[j] * b[i][j];
add[2] = add[2] + kz[j] * b[i][j];
}
kx[i] = h*gammaN*((mu[1] + h*add[1]) * bz - (mu[2] + h*add[2]) * by);
ky[i] = h*gammaN*((mu[2] + h*add[2]) * bx - (mu[0] + h*add[0]) * bz);
kz[i] = h*gammaN*((mu[0] + h*add[0]) * by - (mu[1] + h*add[1]) * bx);
}
for (int l = 0;l < 6;l++){
addmu[0] = addmu[0] + kx[l] * c[l];
addmu[1] = addmu[1] + ky[l] * c[l];
addmu[2] = addmu[2] + kz[l] * c[l];
addmus[0] = addmu[0] + kx[l] * cs[l];
addmus[1] = addmu[1] + ky[l] * cs[l];
addmus[2] = addmu[2] + kz[l] * cs[l];
}
for (int m = 0;m <=2; m++) {
mu4[m] = mu4[m] + addmus[m];
mu5[m] = mu5[m] + addmu[m];
mu[m] = mu4[m];
}
//delete[] mu5;
//delete[] mu4;
//delete[] kx;
//delete[] ky;
//delete[] kz;
//delete[] add;
//delete[] addmu;
//delete[] addmus;
}
a、b、c、cs为Cash-Karp法Bucher表中的值,h为步长,1.0e-8。
static double a[6] = { 1.0 / 5.0, 3.0 / 10.0, 3.0 / 5.0, 1.0, 7.0 / 8.0 };
static double bij[6][6] = { {0.0,0.0,0.0,0.0,0.0,0.0},
{1.0 / 5.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{3.0 / 40.0, 9.0/40.0, 0.0, 0.0, 0.0, 0.0},
{3.0/10.0, -9.0/10.0, 6.0/5.0, 0.0, 0.0, 0.0},
{-11.0/54.0, 5.0/2.0, -70.0/27.0, 35.0/27.0, 0.0},
{1631.0/55296.0, 175.0/512.0, 575.0/13824.0, 44275.0/110592.0, 253.0/4096.0} };
static double c[6] = { 37.0 / 378.0, 0.0, 250.0/621.0, 125.0/594.0, 0.0, 512.0/1771.0 };
static double cs[6] = {2825.0/27648.0, 0.0, 18575.0/48384.0, 13525.0/55296.0, 277.0/14336.0, 1.0/4.0 };
磁场值由插值器在函数外部设置并传入,但只是为了确保它产生振荡,我用恒定磁场对其进行测试,其中 bx 和 by 为 0,bz是5。传入的mu开始为{1,0,0}
现在,当我在函数末尾不使用任何删除命令时,整个程序都在调试器中运行,但是存在非常明显的内存泄漏。
当我只对用 new 初始化的数组使用 delete 时,弹出 window 错误 "Main.exe has triggered a breakpoint." 并且没有其他信息。在输出 window 我得到 “ HEAP[Main.exe]:指定给 RtlValidateHeap 的地址无效(00470000,0016F514) Main.exe 已触发断点。"
当我在函数末尾删除所有这些时,我得到了同样的错误,但 RtlValidateHeap 中的地址不同。
我在发布模式下尝试 运行,并切换我的运行时库,我得到“检测到严重错误 c0000374 Main.exe 已触发断点。"
我是在误用 delete[] 还是我做错了什么?这个错误是什么意思?
double *mu5 = new double[3];
mu5 = mu;
这不会复制数组,这会重新分配指针,然后您尝试删除错误的数组(即作为函数参数的数组而不是您分配的数组)。
这是 C++(和 C)中原始数组的众多错误之一,请使用容器(在本例中为 std::vector<T>
或 std::array<T>
),如果可以的话它们已经过载 operator=
做你期望的事。
也就是说:你为什么要使用动态分配? A) 你知道前面数组的大小,B) 它们不够大以至于溢出堆栈。
您正在删除 add
、addmu
和 addmus
。这些是在堆栈上分配的(而不是在 new
的堆上)。因此,他们不能 delete[]
'。
由于您在编译时知道数组的所有大小并且它们很小,因此您可能应该将它们全部分配到堆栈上。对于动态数组,在 C++ 中,最佳做法是使用像 vector
这样的容器,这使您免于手动使用 delete
的责任。
此外,正如 Borgleader 所说,行
double *mu5 = new double[3];
mu5 = mu;
double *mu4 = new double[4];
mu4 = mu;
没有按照您的期望去做。首先在堆上分配一个double[3]
,它的地址给mu5
。然后,您擦除该地址并用 mu
覆盖它。当您 delete[] mu5
时,您正在释放 mu
指向的内存(这可能不是故意的),而 new double[3]
仍然存在。现在要通过最少的更改更正它,您可以将该代码段替换为
double *mu5 = new double[3];
std::copy(mu, mu + 3, mu5);
double *mu4 = new double[4];
std::copy(mu, mu + 4, mu4);
但是,写这个是C风格,不是C++风格(虽然它使用C++语法)。