提问者:小点点

为什么memset之后delete会失败


下面的代码在删除时会导致核心转储。 但是如果注释掉“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;
}


共3个答案

匿名用户

您创建的是指向不连续内存区域的指针数组

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 {}
}