当我意识到我对指针有很大的误解时,我正在解决一些编程练习。请有人解释一下这段代码导致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;
}
P.S我知道没有理由在这里使用“new”,我只是尽可能地使用这个例子。
答案 0 :(得分:80)
实际上是你标记为导致程序崩溃导致程序崩溃的声明!
当您从idiv
返回时,必须将同一指针传递给delete[]
。
否则程序的行为是 undefined 。
答案 1 :(得分: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;
答案 2 :(得分:18)
在这里不详细讨论具体实现的具体细节,可以通过考虑delete[]
应该做什么来解释崩溃背后的直观原因:
销毁由
创建的数组new[]
- 表达式
你给delete[]
一个指向数组的指针。除此之外,它必须释放它分配的内存以保存该数组的内容。
分配器如何知道要释放什么?它使用您指定的指针作为键来查找包含已分配块的簿记信息的数据结构。在某处,有一种结构存储指向先前分配的块的指针与相关的簿记操作之间的映射。
如果您传递给delete []
的指针不是由相应的new[]
返回的指针,您可能希望此查找会产生某种友好的错误消息,但标准中没有任何内容可以保证这一点。
所以,有可能,给定一个先前未由new[]
分配的指针,delete[]
最终会看到一些真正不是一致的簿记结构的东西。电线越过。随之而来的是崩溃。
或者,您可能希望delete[]
会说“嘿,看起来这个指针指向我之前分配的区域内的某个地方。让我回去找到我在分配该区域时返回的指针用它来查阅簿记信息“但是,标准中没有这样的要求:
对于第二个(数组)形式,expression必须是空指针值或先前由new-expression的数组形式获得的指针值。如果 表达式是其他 ,包括它是否是由非数组形式的new-expression获得的指针, 行为是未定义的 即可。 [强调我的]
在这种情况下,你很幸运,因为你发现你瞬间做错了什么。
PS:这是一个手写的解释
答案 3 :(得分:8)
您可以在块内增加指针并使用该增量指针访问块的不同部分,这很好。
但是,您必须传递删除从New获得的指针。不是它的增量版本,不是通过其他方式分配的指针。
为什么呢?这个问题的答案是因为这就是标准所说的。
实际的答案是,为了释放一块内存,内存管理器需要有关该块的信息。例如,它的开始和结束,以及相邻的块是否空闲(通常是内存管理器将组合相邻的空闲块)以及它属于哪个领域(对于锁定多线程内存管理器很重要)。
此信息通常紧接在分配的内存之前存储。内存管理器将从指针中减去一个固定值,并在该位置查找分配元数据的结构。
如果传递的指针不指向已分配内存块的开头,则内存管理器会尝试执行减法并读取它的控制块,但它最终读取的内容不是有效的控制块。
如果你很幸运,那么代码会很快崩溃,如果你运气不好,那么最终会导致细微的内存损坏。