为什么在memset之后删除失败

时间:2020-07-04 09:09:31

标签: c++

以下代码在删除时将导致核心转储。但是,如果注释掉“ 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 个答案:

答案 0 :(得分:3)

p[i]中已分配的内存不一定是连续的。因此,调用memset清除p[i]中所有分配的内存将触及一部分不适合您的内存(造成分段错误的主要原因)。如果要将它们全部设置为零,则必须遍历它们:

for (int i=0; i<n; i++) {
     memset(p[i], 0, n*sizeof(int));
}

答案 1 :(得分:1)

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

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[]调用就是对这种“内存损坏”的典型反应。

请注意,您不需要memset即可在C ++中使用数组进行零初始化,只需在创建时对其进行初始化:

int **p = new int* [n];
for (int i=0; i<n; i++) {
    p[i] = new int [n]();
}

如果您希望数组为连续的二维数组,其中每个子数组都与下一个相邻(标准工具均未提供),则可以使用“放置新方法”

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

或更妙的是,如果可能的话,请避免使用裸露的指针或使用户接触此类代码。使用标准库类型或您自己的类型的封装来隐藏该“代码戈尔”。这种公开代码的问题在于,如果有什么事情会中断执行,例如,没有任何程序可以释放内存。一个例外。

答案 2 :(得分:0)

memset

将值ch转换为无符号char,并将其复制到dest所指向对象的每个第一个计数字符中。如果对象是可能重叠的子对象或不是TriviallyCopyable(例如,标量,C兼容结构或普通可复制类型的数组),则该行为是不确定的。 如果count大于dest指向的对象的大小,则行为是不确定的。

每次调用new时,都会返回一个新的内存块。它不必完全在上一个块的后面。

因此,存储器不必像memset那样必须是连续的,并且memset正在未分配给程序的存储器中写入,因此会发生崩溃。

要正确将内存初始化为零,请在新变量后添加括号:

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