delete[]如何知道数组的大小?

17

我很好奇delete[]如何确定已分配内存的大小。当我这样做:

int* table = new int[5];
delete[] table;

我理解表的内存已被释放。但如果我将指针重新分配给另一个表,会发生什么?

int* table = new [5];
int* table2 = new [9];
table = table2;
delete[] table;

我将释放一个大小为5或9的表格吗?我想知道new[]和delete[]如何共享有关它们大小的信息。或者我可能在这里缺少一些重要的东西。


复制(或者相似)自:https://dev59.com/SEbRa4cB1Zd3GeqPwgl2#377208,请勿解释。 - Binary Worrier
7
我不同意,这更具体。 - Doug T.
3
这很大程度上是https://dev59.com/4HVC5IYBdhLWcg3wvT1a的重复。 - Jonathan Leffler
抱歉,问题重复了。 - Lucas
1
@curiousguy:那是三年前的事了。但是http://msdn.microsoft.com/en-us/library/bebs9zyz(v=vs.80).aspx有一些关于MSVC如何处理调试堆栈的很棒的信息。 - Mooing Duck
显示剩余5条评论
8个回答

24

它将删除一个大小为9的数组。 它删除由指针指向的数组。

存储大小信息的方式未指定,因此每个编译器可能采用不同的实现方式,但常见的方法是在数组之前分配一个额外的块。也就是说,当你这样做时:

int* table = new int[5];

实际上,它会分配一个由6个整数组成的数组,并在第一个元素中存储数组大小。然后返回指向第二个元素的指针。所以,要找到大小,delete[] 只需要读取 table[-1]。

这是一种常见的方法,但语言标准并没有规定必须按照这种方式进行。只要能够正常工作即可。

另一种方法可能是使用数组的地址作为全局哈希表中的键。只要能够产生正确的结果,任何方法都是有效的。


1
谢谢,这个解释对我来说很有意义。虽然我希望能选择多个答案作为被接受的答案。 - Lucas

20

C++ FAQ lite的第16.14节回答了这个问题:

有两种流行的技术可以实现这一点。这两种技术都被商业级编译器所使用,都有权衡之处,而且都不完美。这些技术是:

* Over-allocate the array and put n just to the left 
  of the first Fred object.
* Use an associative array with p as the key and n as the value.

1
你能提供一个带有关联数组的编译器吗? - curiousguy
根据FAQ中的链接,CFront使用以下内容: http://www.parashift.com/c%2B%2B-faq-lite/compiler-dependencies.html#faq-38.8 - Doug T.
你指的是从来没有现代模板的编译器,早在15年前就已经过时了,最后一次发布于1991年的编译器:"_Bjarne Stroustrup指出,“警告Cfront 3是预标准版本,强烈不建议使用或进一步开发”_。 - curiousguy
@curiousguy 是的,就是那个。C++ FAQ 上可能需要更新这一点。 - Doug T.
1
有人知道在gcc中如何处理x86和ARM以及Microsoft Visual Studio编译器吗? - killdaclick

4
这是如何实现的是编译器特定的细节。但是,在没有内存损坏的情况下,调用delete[]将始终删除正确数量的元素。有多种方法可以实现这一点,但一个简单的方法是在内存中隐藏长度。
以下是演示目的的一个好而简单的实现方式。假设你的代码调用new int[10]。编译器分配的不是10 * sizeof(int),而是(10 * sizefo(int))+sizeof(size_t)。然后它会返回一个指针,该指针从起始位置偏移size_t。在初始的size_t空间内,它写入数字10。现在当你调用delete[]并传递一个指针时,编译器只需向后回退size_t字节,并找到要删除的元素数。

哎呀,你比我快了 - 我本来也要发同样的例子。 - gnud
这是将其隐藏在分配的内存中的示例,而不是“隐藏在指针中”。 - Dan Moulding
谢谢。我不知道new[]会分配额外的内存来存储大小。 - Lucas
@Jared:呃……当向后读取大小信息时,编译器如何知道从哪里开始? - Nav

2

delete[]的工作方式取决于实现,但是全局new运算符以某种方式将描述符与分配的内存关联起来(在大多数情况下,它会预先分配给已分配的内存,或者存储在查找表中)。描述符包含分配的内存的实际大小。

在您的第二个代码示例中,delete[]将正确删除九个int元素的数组,并且原始的五个元素的数组将被泄漏。


1
机制是与实现相关的,但它不会被你在示例中重新分配指针所“欺骗”。它将释放大小为9的数组,就像你告诉它的那样(在这种情况下,将会有一个内存泄漏,因为大小为5的数组现在没有任何指向它的东西)。

0
你可以想象系统将表的大小存储在这样一个结构中:
struct MemoryAllocated
  {
  size_t sizeOfMemory;
  char* yourData;
  }

每次分配内存时,系统都会返回指向“yourData”的指针。而每次释放内存时,系统会移动指针以获取“sizeOfMemory”。请参阅std::allocator。

这个回答只会让人更加困惑。系统如何“移动” yourData 来达到 sizeOfMemory - 0xF

0
在使用new[]/delete[]的情况下,内存方面发生的事情与使用new/delete的情况相同/类似...大小信息存储在(较大的)分配块本身中。对于数组而言,有趣的是它还使用大小信息来知道需要调用多少个对象的析构函数。

0
我的猜测是,new[]实际上分配的数据比看起来多。它可能在返回指针之前有几个字节,用于告诉数组中有多少项。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接