我正在使用这段代码:
try
{
int* myTestArray = new int[2];
myTestArray[4] = 54;
cout << "Should throw ex " << myTestArray[4] + 1 << endl;
}
catch (exception& exception)
{
cout << "Exception content: " << exception.what() << endl;
}
对我来说真正的好处是,为什么不抛出异常,因为它被访问了一个未分配的索引......以及为什么要打印55? C ++会自动增加数组的大小吗?
答案 0 :(得分:10)
这里有一个未说明的,不正确的假设。这个假设是C ++实际上对你用内存做了什么感到很遗憾。 C ++与它的C祖先一样,具有完全未经检查的内存模型。你在这里经常被称为缓冲区溢出,并且是无数错误的来源,包括一些可怕的安全漏洞。
以下是您的代码所说的内容:
myTestArray
是内存中某个位置的名称,其大小足以容纳int
的地址。
已经在堆上为它分配了两个int
的内存。 [并且addreress被放入位置myTestArray
。没关系,但这可能会让它更清晰。] (可能还有16个字节的开销,但我们现在不关心。)
然后您将值54
保留在int
中包含的地址的myTestArray
内存位置。
查看该位置,添加1并打印结果。
你证明C(++)确实不在乎。
现在,在大多数情况下,底层内存管理和运行时系统都不会让你逃脱它;你将违反它的假设并得到分段错误或类似的东西。但是在这种情况下,你还没有达到边界,很可能是因为你正在调整malloc正在使用的数据结构来管理堆。你正在逃避它,因为程序其余部分的堆没有发生任何事情。但是为了一个真正的美好时光,编写一个执行此代码的小循环,释放myTestArray
并重新分配它。在程序爆炸之前我不会超过10次迭代,而且可能不会生成两次。
答案 1 :(得分:9)
无法保证访问未分配的内存会引发异常。
实际上并不能保证做任何事情,因为这是未定义的行为。什么事情都可能发生。小心鼻子恶魔。
它打印55因为你刚存储54,取回它然后打印54 + 1。它根本不能保证打印55,尽管这通常会在实践中发生。这次它奏效了。
答案 2 :(得分:1)
你可以写出数组范围,但不能保证工作,并且不保证数据在那里是持久的,因为其他东西可以覆盖它。
这不是一个好主意,因为没有例外,可能很难找到错误。
当读取那个内存时,你会从其他一些程序或以前使用过的内存中留下一些随机垃圾,所以它真的可以是任何东西。
答案 3 :(得分:1)
正如其他人所说,这是未定义的行为,但我认为更多的信息可能有所帮助。 myTestArray
在类型意义上不是“数组”,具有特殊运算符等。它只是指向内存中某个位置的指针。表达式myTestArray[4]
只是*(myTestArray+4)
的简写 - 它返回对4 * sizeof(int)
过去myTestArray
的内存位置的引用。如果您想要边界检查,则必须使用std::vector<int>::at()
。
答案 4 :(得分:1)
知道这里发生了什么肯定很难做到。但我可以给你一个粗略的想法。
大多数操作系统都具有内存分配的最小大小。在Unix中,它是本机页面大小。在x86和amd64系统上,这是4 kB。在Windows中它是64 kB(我认为)。
malloc
和new
使用的内存分配器从此大小的块中获取操作系统的内存。它设置数据结构(通常是链表,有时是位图或树),并分发所需大小的小块。
另一个令人困惑的事情是,在程序开始运行main()
之前,它已经运行了相当多的其他代码并分配了内存。对于std::cout
和其他静态和全局对象,以及共享库链接。
但是假设当你调用new
时,你的程序首先得到一个4 kB的块,并给你一个指向它的8个字节的指针(两个整数)。你的程序分配了整个4 kB,你可以在那里写入而不会崩溃。但是,如果再次致电new
会怎样?内存分配器很可能在某个地方写入了一些重要的跟踪信息到4 kB。下一个字节可能是以下块的大小。将54写入其中可能会使其认为它具有比它更多或更少的内存。或者那些字节可能是指向下一个可用内存块的指针,而你的54将导致下一次内存分配使程序崩溃。
答案 5 :(得分:0)
访问数组超出范围是未定义的行为。因此,55是许多可能的结果之一,这里没有什么令人惊讶的。
C ++标准版n3337 § 5.7添加运算符
5)当添加或减去具有整数类型的表达式时 从指针开始,结果具有指针操作数的类型。如果 指针操作数指向数组对象的元素和数组 足够大,结果指向一个偏离的元素 原始元素使得下标的差异 结果和原始数组元素等于整数表达式。 换句话说,如果表达式P指向一个的第i个元素 数组对象,表达式(P)+ N(等效地,N +(P))和(P)-N (其中N的值为n)分别指向i + n和i - 数组对象的第n个元素,只要它们存在即可。而且,如果 表达式P指向数组对象的最后一个元素,即 表达式(P)+1指向数组对象的最后一个元素, 如果表达式Q指向一个数组的最后一个元素 对象,表达式(Q)-1指向数组的最后一个元素 宾语。如果指针操作数和结果都指向元素 相同的数组对象,或一个超过数组的最后一个元素 对象,评估不得产生溢出; 否则, 行为未定义。