删除[]的行为

时间:2012-03-18 21:59:46

标签: c++ delete-operator

  

可能重复:
  C++ delete - It deletes my objects but I can still access the data?

我很好奇为什么我使用以下(相当人为的)代码获得以下行为。我在用 关于Ubuntu 10.10的gcc 4.4.5

#include <iostream>

int main(int argc, char *argv[])
{
  int N = 5;
  int *myarr = new int[N];//create an integer array of size 5

  for (int i = 0; i < N; ++i)
    {
      myarr[i] = 45;
      std::cout << myarr[i] << std::endl;
    }  


  delete[] myarr;//kill the memory allocated by new
  std::cout << "Let's see if the array was deleted and if we get a segfault \n" ;

  for (int i = 0; i < N; ++i)
    {
      std::cout << myarr[i] << std::endl;
    }


  return 0;
}

即使使用-Wall标志,代码也会编译。输出是

Desktop: ./a.out
45
45
45
45
45
Let's see if the array was deleted and if we get a segfault 
0
0
45
45
45
Desktop: 

为什么仍然可以访问myarr数组,就像它在没有段错误的情况下被删除一样? 此外,即使myarr数组被删除,为什么值45似乎仍然在2,3和4位正确打印。

简而言之delete[]真正在这里做什么?

7 个答案:

答案 0 :(得分:3)

delete[]为数组中的每个元素调用析构函数,并告诉您的计算机您将不再使用该内存。记忆仍然在那里,你不允许使用它。

您所看到的是未定义的行为。它可能会以各种方式失败,但由于你的程序很短,它可能会像你在大多数时候看到的那样工作。

答案 1 :(得分:2)

释放内存保证在以后尝试访问该内存时会出现段错误。基本上,在释放内存后访问内存会导致未定义的行为 - 任何事情都可能发生。

在您的情况下,delete[]运算符释放内存并将其返回到运行时库。但是,运行时库会挂起到内存中,并且不会立即将其返回给操作系统(毕竟,您可能很快就会再次需要它)。运行时库将使用应用程序已释放的内存来管理自己的数据结构。例如,您看到的零可能是自由列表结构的一部分。

答案 2 :(得分:1)

运算符delete[]仅释放内存。 delete[]未定义行为后访问内存。

myarr指向的内存可能仍会包含您暂时分配的值。该内存地址将包含相同的值,直到有人向其写入内容。

答案 3 :(得分:1)

内存被取消分配,其中一些已经消失了,你可以看到。它仍然可以被访问,但你的程序没有崩溃这一事实主要是因为没有足够的时间为这个内存分配不同的对象,或者你没有尝试做任何顽皮的事情,比如在其中使用一个值作为缓冲区或其他东西的偏移量。

答案 4 :(得分:0)

您认为delete[]做了什么?它调用数组中所有对象的析构函数,然后释放它。你的myarr指针现在指向释放的内存,并且是一个无效的指针。

显然,内存尚未被重用,因此它仍然包含释放之前存在的数据,但这是未定义的行为,这意味着您不能依赖它。如果你这样做,你有一天会有各种令人兴奋的难以发现的错误。

答案 5 :(得分:0)

delete []调用析构函数,然后告诉系统先前分配的内存不会被进一步使用,但指针仍然指向起始地址。

#include <iostream>

int main(int argc, char *argv[])
{
  int N = 5;
  int *myarr = new int[N];//create an integer array of size 5

  for (int i = 0; i < N; ++i)
    {
      myarr[i] = 45;
      std::cout << myarr[i] << std::endl;
    } 


  delete[] myarr;//kill the memory allocated by new
  std::cout << "Let's see if the array was deleted and if we get a segfault \n" ;

  int *dumb = new int[N];

  dumb[3]=0;

  for (int i = 0; i < N; ++i)
    {
      std::cout << myarr[i] << std::endl;
    }


  return 0;
}

,输出为:

45
45
45
45
45
Let's see if the array was deleted and if we get a segfault 
0
45
45
0
45

您没有出现分段错误,因为内存太小而且可能未被其他进程保留。但试试这个:

#include <iostream>

int main(int argc, char *argv[])
{
  int N = 50000;
  int *myarr = new int[N];//create an integer array of size 5

  for (int i = 0; i < N; ++i)
    {
      myarr[i] = 45;
      std::cout << myarr[i] << std::endl;
    }


  delete[] myarr;//kill the memory allocated by new
  std::cout << "Let's see if the array was deleted and if we get a segfault \n" ;
  std::cin >> N;
  int *dumb = new int[N];

  dumb[3]=0;

  for (int i = 0; i < N; ++i)
    {
      std::cout << myarr[i] << std::endl;
    }


  return 0;
}

并输入一些数字,例如9000

答案 6 :(得分:0)

这里所写的一切都是真的,我只想更多地了解为什么你所做的是未定义的行为。

  1. 就进程的内存分配而言,操作系统不是零售商,你不能得到10个字节,你只能在页面中请求内存(通常是1页= 4KiB)。 / LI>
  2. 但是流程在较小的块上运行,这就是为什么每个流程都有自己的“分配器” - 这是运营商 new delete 的实现。这两个野兽位于你的程序和操作系统之间。他们在页面中请求内存(例如,请给我3页内存),并为您的程序(流程)管理这些页面。
  3. 当你想分配10个字节时,运算符 new 将分配(至少)一个页面(如果需要)并给你一个指向该页面内存的指针,它也会记住,从那个地址(它给你),使用了10个字节。
  4. 当你不再需要这10个字节时,你可以调用operator delete - 这个野兽会注意到不再使用10个字节(在上面提到的页面上)。 如果不再使用整个页面可以(但不一定)被释放(返回系统)。当你尝试访问释放的内存时会发生这种情况 - 它会给你分段错误(页面不再由你的进程拥有),但如果页面没有返回给系统,它仍然可供你使用,(但你肯定不知道
  5. 即使内存仍然可访问,您也不应该访问它,因为:
    • 内容可以重复用于其他分配,这意味着它可以更改
    • 内容可以(通常是)由删除的内部结构使用。如果您写入已删除的内存,可能会导致这些结构损坏,这通常会导致完全意外的地方发生段错误(例如,在代码中其他一些完全不相关的地方的删除[]上)。
  6. 请记住,我上面写的所有内容都只是非常简化的版本,它比那复杂得多,但我希望它会让你知道这一切是如何工作的“引擎盖下”。 阿门。