为什么我的程序在递增指针然后删除它时会崩溃?

Bee*_*oot 30 c++ pointers dynamic-memory-allocation delete-operator

当我意识到我对指针有很大的误解时,我正在解决一些编程练习.请有人解释一下这段代码导致C++崩溃的原因.

#include <iostream>

int main()
{
    int* someInts = new int[5];

    someInts[0] = 1; 
    someInts[1] = 1;

    std::cout << *someInts;
    someInts++; //This line causes program to crash 

    delete[] someInts;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

PS我知道没有理由在这里使用"new",我只是尽可能地使用这个例子.

Bat*_*eba 80

它实际上是你标记为导致程序崩溃导致程序崩溃的声明!

必须在同一指针传递给delete[]当你需要回来new[].

否则,程序的行为是不确定的.

  • @ Phil1970:对于数组删除(`delete []`),它必须是完全相同的指针类型,不允许多态. (14认同)

小智 33

问题是,someInts++;您将数组的第二个元素的地址传递给您的delete[]语句.您需要传递第一个(原始)元素的地址:

int* someInts = new int[5];
int* originalInts = someInts; // points to the first element
someInts[0] = 1;
someInts[1] = 1;

std::cout << *someInts;
someInts++; // points at the second element now

delete[] originalInts;
Run Code Online (Sandbox Code Playgroud)


Sin*_*nür 19

在不进入具体实现的具体细节的情况下,可以通过考虑delete[]应该做什么来简单解释崩溃背后的直观原因:

销毁由new[]-expression 创建的数组

你给delete[]一个指向数组的指针.除此之外,它必须释放它分配的内存以保存该数组的内容.

分配器如何知道要释放什么?它使用您指定的指针作为键来查找包含已分配块的簿记信息的数据结构.在某处,存在一种结构,其存储指向先前分配的块的指针与相关的簿记操作之间的映射.

如果您传递给的指针delete []不是由相应的指针返回的,那么您可能希望此查找导致某种友好的错误消息new[],但标准中没有任何内容可以保证这一点.

因此,有可能,给定一个之前没有分配过的指针new[],delete[]最终会看到一些真正不是一致的簿记结构的东西.电线越过.随之而来的是崩溃.

或者,您可能希望这样delete[]说"嘿,看起来这个指针指向我之前分配的区域内的某个地方.让我回去找到我在分配该区域时返回的指针并使用它来查找簿记信息"但是,标准中没有这样的要求:

对于第二个(数组)形式,expression必须是空指针值或先前由new-expression的数组形式获得的指针值.如果expression是其他任何东西,包括它是否是由new-expression的非数组形式获得的指针,则行为是未定义的.[强调我的]

在这种情况下,你很幸运,因为你发现你瞬间做错了什么.

PS:这是一个手工波浪的解释

  • 有时候手工波浪的解释没有错.其他答案很简洁,但我终于得到了_why_它是通过阅读这个来实现的.(和@ plugwash的:) (2认同)

plu*_*ash 8

您可以在块中递增指针并使用该递增指针访问块的不同部分,这很好.

但是,您必须传递删除从New获得的指针.不是它的增量版本,不是通过其他方式分配的指针.

为什么?这个问题的答案是因为这就是标准所说的.

实际的答案是因为释放一块内存,内存管理器需要有关块的信息.例如,它的开始和结束,以及相邻的块是否空闲(通常是内存管理器将组合相邻的空闲块)以及它属于哪个竞技场(对于锁定多线程内存管理器很重要).

该信息通常紧接在分配的存储器之前存储.内存管理器将从指针中减去固定值,并在该位置查找分配元数据的结构.

如果传递的指针不指向已分配内存块的开头,则内存管理器会尝试执行减法并读取它的控制块,但它最终读取的内容不是有效的控制块.

如果你很幸运,那么代码会很快崩溃,如果你运气不好,那么你最终可能会出现细微的内存损坏.