访问未分配的内存 C++

Accessing unallocated memory C++

我有这段代码:

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 是 print ? C++ 是否自动增加了数组的大小?

访问未分配的内存不保证会抛出异常。

它实际上不能保证做任何事情,因为那是未定义的行为。什么事情都可能发生。小心鼻炎。

它打印 55 因为你刚刚存储了 54,取回它然后打印 54+1。根本不能保证打印出 55,尽管这在实践中经常会发生。这次成功了。

超出范围访问数组是未定义的行为。因此 55 是许多可能的结果之一,这里没有什么令人惊讶的。

C++ 标准 n3337 § 5.7 加法运算符

5) When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

您可以在数组范围之外写入,但不能保证它会工作,并且不能保证数据在那里持久存在,因为其他东西可以覆盖它。

这根本不是一个好主意,而且由于没有例外,可能很难找到错误。

读取该内存时,您将提取一些随机垃圾,这些垃圾是其他程序或之前使用内存的任何东西遗留下来的,所以它真的可以是任何东西。

正如其他人所说,这是未定义的行为,但我认为更多信息可能会有所帮助。 myTestArray 不是类型意义上的 "Array",具有特殊运算符等。它只是指向内存中某个位置的指针。表达式 myTestArray[4] 只是 *(myTestArray+4) 的简写 - 它返回对 4 * sizeof(int) 过去 myTestArray 的内存位置的引用。如果你想要边界检查,你必须使用 std::vector<int>::at().

这里有一个未说明的、不正确的假设。这个假设是 C++ 实际上对你对内存所做的事情给出了该死的看法。 C++ 与其 C 祖先一样,具有完全未经检查的内存模型。你在这里遇到的通常称为缓冲区溢出,是无数错误的来源,包括一些可怕的安全漏洞。

这是您的代码的真实含义:

  • myTestArray 是内存中足以容纳 int.

  • 地址的位置的名称
  • 两个 int 的内存已经在堆上分配给它。 [并且该地址被放入位置myTestArray。没关系,但这可能使它更清楚。](可能还有 16 个字节的开销,但我们现在不关心它。)

  • 然后您将值 54 粘贴到内存位置 4 ints 从包含在 myTestArray.

  • 查看那个位置,加 1 并打印结果。

你在证明 C(++) 确实不在乎。

现在,在大多数情况下,底层内存管理和 运行 时间系统不会让您逃脱;您将违反它的 假设并得到分段错误或类似的东西。但在这种情况下,您还没有触及边界,很可能是因为您正在处理 malloc 在幕后用来管理堆的数据结构。你逃脱了它,因为对于程序的其余部分,堆没有发生任何事情。但真正的好时光,写一个小循环来执行这段代码,释放 myTestArray 并重新分配它。我敢打赌它不会 运行 在程序崩溃之前进行超过 10 次迭代,并且可能不会产生两次。

肯定知道这里发生了什么是非常困难的。不过我可以给你一个大概的概念。

大多数操作系统都有内存分配的最小大小。在 Unix 中,它是本机页面大小。在 x86 和 amd64 系统上,这是 4 kB。在 Windows 中是 64 kB(我认为)。

mallocnew 使用的内存分配器以这种大小的块为单位从操作系统获取内存。它设置数据结构(通常是链表,有时是位图或树)并分发请求大小的小块。

另一件令人困惑的事情是,在您的程序甚至开始 运行ning main() 之前,它已经 运行 相当多的其他代码和分配的内存。对于 std::cout 和其他静态和全局对象,以及共享库链接。

但假设当您调用 new 时,您的程序首先获得一个 4 kB 的块,并为您提供一个指向其中 8 个字节(两个整数)的指针。您的程序分配了整个 4 kB,您可以在那里写入而不会崩溃。但是,如果您再次调用 new 会怎样?内存分配器很可能在该 4 kB 的某处写入了一些重要的跟踪信息。下一个字节可能是下一个块的大小。向其中写入 54 可能会使它认为它的内存比实际多或少。或者那些字节可能是指向下一个空闲内存块的指针,而你的 54 将导致下一个内存分配使程序崩溃。