下面的代码在删除时会导致核心转储。 但是如果注释掉“memset”,那么运行就很好。 看起来像是memset做错了什么。 下面的代码有什么问题?
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int n= 5;
int **p = new int* [n];
for (int i=0; i<n; i++) {
p[i] = new int [n];
}
// if comment out this line, it is good to run.
memset(&p[0][0], 0, n*n*sizeof(int));
// core dump here
for (int i=0; i<n; i++) {
delete [] p[i];
}
delete [] p;
return 0;
}
您创建的是指向不连续内存区域的指针数组
int **p = new int* [n];
for (int i=0; i<n; i++) {
p[i] = new int [n];
}
这里P[0]
点在第一个区域,P[1]
点在第二个区域,P[n]
点在最后。 它们不是同一个对象,所以从语言律师的角度来看,这种memset
调用是一种未定义的行为。
memset(&p[0][0], 0, n*n*sizeof(int)); // Out of bound
&p[0][0]
指向n
元素的数组对象的第一个元素(大小n*sizeof(int)
)。 在您破坏规则之后允许发生的任何奇怪的事情,破坏的delete[]
调用就是对这种“内存损坏”的典型反应。
注意,对于C++中的数组,您不需要memset
进行零初始化,您所需要的只是在创建时初始化它们:
int **p = new int* [n];
for (int i=0; i<n; i++) {
p[i] = new int [n]();
}
如果您希望您的数组是连续的二维数组,其中每个子数组都与下一个相邻(没有任何标准工具提供这样的),您可以使用placement new方法。
int **p = new int* [n];
int *pool = new int [n*n]; // the whole array will be here
for (int i=0; i<n; i++) {
p[i] = new (pool + i*n) int [n]();
// Creating subobject arrays using placement parameter
// In this case parameter is a pointer to memory storage
// where new expression would create an object.
// No memory allocation is happening here.
}
....
delete [] p; // deleting array of pointers
delete [] pool; // deleting memory pool
或者更好的是,如果可能的话,避免使用裸指针或将用户暴露在这样的代码中。 使用封装,无论是标准库类型还是您自己的类型来隐藏“代码戈尔”。 这样公开的代码的问题在于,如果某些事情会中断执行,例如异常,则没有释放内存的过程。
P[i]
S中分配的内存不一定是连续的。 因此,调用memset
来清除p[i]
s中分配的全部内存将触及不适合您的部分内存(分段错误的主要原因)。 如果要将它们全部设置为零,则必须迭代它们:
for (int i=0; i<n; i++) {
memset(p[i], 0, n*sizeof(int));
}
Memset
将值ch转换为无符号char,并将其复制到dest指向的对象的每个第一个计数字符中。 如果对象是一个潜在重叠的子对象,或者不是简单可复制的(例如,标量,C兼容结构或简单可复制类型的数组),则行为是未定义的。 如果count大于dest指向的对象的大小,则行为未定义。
每次调用new
时,都会返回一个新的内存块。 它不一定要正好在前一个区块的后面。
因此内存不必是连续的,这是memset
所要求的,并且memset正在未分配给程序的内存中写入,因此会发生崩溃。
要正确地对内存进行零初始化,请在new:
int **p = new int* [n] (); // With C++11 and later can also be {}
for (int i=0; i<n; i++)
{
p[i] = new int [n] (); // With C++11 and later can also be {}
}