为什么 valgrind 和 gdb 指向不同的代码行?或者:如何在循环中调用指针的 malloc() 和 free() 指针?
Why do valgrind and gdb point to different lines of code? Or: How to malloc() and free() pointer of pointer in loop?
以下代码中的循环可以执行几次但随后会崩溃。
#include <cstdlib>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[])
{
//not needed but program will not crash if I remove it
int blocksize=stoi(argv[1]);
//typical value 70-100
int min_length=stoi(argv[2]);
for(int i=0;i<250000;i++)
{
//Allocate memory for new integer array[row][col]. First allocate the memory for the top-level array (rows).
int **output_std = (int**) malloc(20*sizeof(int*));
//Allocate a contiguous chunk of memory for the array data values.
output_std[0] = (int*) malloc( min_length*20*sizeof(int) );
//Set the pointers in the top-level (row) array to the correct memory locations in the data value chunk.
for (int k=1; k < 20; k++)
{
output_std[k] = output_std[0]+k*min_length;
}
//do something with output_std
//free malloc'd space
free(output_std[0]);
for(int k=0;k<20;k++)
{
output_std[i]=NULL;
}
free(output_std);
output_std=NULL;
}
return 0;
}
使用 GDB
调试指向第 36 行:free(output_std);
。
使用 valgrind
进行调试会产生以下错误:
nvalid write of size 8
==32161== at 0x4031A0: main (test.cpp:31)
==32161== Address 0x82f2620 is 0 bytes after a block of size 160 alloc'd
==32161== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32161== by 0x403159: main (test.cpp:16)
第 16 行是:int **output_std = (int**) malloc(20*sizeof(int*));
第 31 行是:free(output_std[0]);
为什么我的代码中的错误位置不同?
遇到这种情况怎么办?
(如何修复我的代码?)
编辑:线条正确。我需要这样一个第三方库的对象。
valgrind
用自己的检测版本替换内存分配和释放函数。您可以在输出中看到:
at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
这就是为什么在 valgrind
下应用程序可能会在另一个地方崩溃。
Valgrind 通常可以更早地发现问题。这就是使用它的意义所在。 Valgrind 经常抓住问题的根源(或更接近根源),而在 GDB 中你只能看到结果。
在您的情况下,问题的根源是越界写入数组导致的堆内存损坏。结果是堆损坏导致 free
内部崩溃。 Valgrind 抓住了前者。当你 运行 你的程序时(例如在 GDB 下)你只能看到后者。
在你的代码中
for(int k=0;k<20;k++)
{
output_std[i]=NULL;
}
预期的迭代变量是 k
。但是您正在 i
访问您的数组。此时 i
显然是 20
,这导致越界访问被 valgrind 捕获。
我想说这个循环无论如何都是毫无意义的:你正试图将你即将释放的内存归零。人们可以提供一些关于为什么它可能有意义的论据......但是在内存释放函数本身内部,在库的调试版本中,这样的事情更合适。在用户级代码中,它只会用不必要的噪音使代码混乱。
P.S。无论如何,您显然 post 编辑了无效的行号。如果 free(output_std)
是第 36 行,那么 valgrind 应该将违规行视为 34,而不是 31。请下次 post 准确代码,力求准确识别违规行。
以下代码中的循环可以执行几次但随后会崩溃。
#include <cstdlib>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[])
{
//not needed but program will not crash if I remove it
int blocksize=stoi(argv[1]);
//typical value 70-100
int min_length=stoi(argv[2]);
for(int i=0;i<250000;i++)
{
//Allocate memory for new integer array[row][col]. First allocate the memory for the top-level array (rows).
int **output_std = (int**) malloc(20*sizeof(int*));
//Allocate a contiguous chunk of memory for the array data values.
output_std[0] = (int*) malloc( min_length*20*sizeof(int) );
//Set the pointers in the top-level (row) array to the correct memory locations in the data value chunk.
for (int k=1; k < 20; k++)
{
output_std[k] = output_std[0]+k*min_length;
}
//do something with output_std
//free malloc'd space
free(output_std[0]);
for(int k=0;k<20;k++)
{
output_std[i]=NULL;
}
free(output_std);
output_std=NULL;
}
return 0;
}
使用 GDB
调试指向第 36 行:free(output_std);
。
使用 valgrind
进行调试会产生以下错误:
nvalid write of size 8
==32161== at 0x4031A0: main (test.cpp:31)
==32161== Address 0x82f2620 is 0 bytes after a block of size 160 alloc'd
==32161== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32161== by 0x403159: main (test.cpp:16)
第 16 行是:int **output_std = (int**) malloc(20*sizeof(int*));
第 31 行是:free(output_std[0]);
为什么我的代码中的错误位置不同?
遇到这种情况怎么办?
(如何修复我的代码?)
编辑:线条正确。我需要这样一个第三方库的对象。
valgrind
用自己的检测版本替换内存分配和释放函数。您可以在输出中看到:
at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
这就是为什么在 valgrind
下应用程序可能会在另一个地方崩溃。
Valgrind 通常可以更早地发现问题。这就是使用它的意义所在。 Valgrind 经常抓住问题的根源(或更接近根源),而在 GDB 中你只能看到结果。
在您的情况下,问题的根源是越界写入数组导致的堆内存损坏。结果是堆损坏导致 free
内部崩溃。 Valgrind 抓住了前者。当你 运行 你的程序时(例如在 GDB 下)你只能看到后者。
在你的代码中
for(int k=0;k<20;k++)
{
output_std[i]=NULL;
}
预期的迭代变量是 k
。但是您正在 i
访问您的数组。此时 i
显然是 20
,这导致越界访问被 valgrind 捕获。
我想说这个循环无论如何都是毫无意义的:你正试图将你即将释放的内存归零。人们可以提供一些关于为什么它可能有意义的论据......但是在内存释放函数本身内部,在库的调试版本中,这样的事情更合适。在用户级代码中,它只会用不必要的噪音使代码混乱。
P.S。无论如何,您显然 post 编辑了无效的行号。如果 free(output_std)
是第 36 行,那么 valgrind 应该将违规行视为 34,而不是 31。请下次 post 准确代码,力求准确识别违规行。