为什么C ++中的new []运算符实际上创建了一个长度为+ 1的数组?例如,请参阅以下代码:
#include <iostream>
int main()
{
std::cout << "Enter a positive integer: ";
int length;
std::cin >> length;
int *array = new int[length]; // use array new. Note that length does not need to be constant!
//int *array;
std::cout << "I just allocated an array of integers of length " << length << '\n';
for (int n = 0; n<=length+1; n++)
{
array[n] = 1; // set element n to value 1
}
std::cout << "array[0] " << array[0] << '\n';
std::cout << "array[length-1] " << array[length-1] << '\n';
std::cout << "array[length] " << array[length] << '\n';
std::cout << "array[length+1] " << array[length+1] << '\n';
delete[] array; // use array delete to deallocate array
array = 0; // use nullptr instead of 0 in C++11
return 0;
}
我们动态创建一个长度为“length”的数组,但我们能够在索引长度+ 1处分配一个值。如果我们尝试长度+2,我们会收到错误。
这是为什么?为什么C ++使长度=长度+ 1?
答案 0 :(得分:5)
没有。您可以计算地址 array + n
,以便检查另一个地址是否小于它。尝试访问元素array[n]
是未定义的行为,这意味着程序变得毫无意义,并允许编译器执行任何操作。字面意思; GCC的一个旧版本,如果看到#pragma
指令,则在终端上开始一个roguelike游戏。 (谢谢,Revolver_Ocelot,提醒我:这是技术上实现定义的行为,是一个不同的类别。)即使计算地址array + n + 1
,未定义的行为。
因为它可以做任何事情,你试过的特定编译器决定让你自己开枪。例如,如果数组之后的下两个单词是堆中另一个块的标头,则可能会出现内存损坏错误。或者,编译器可能将数组存储在内存空间的顶部,地址&array[n+1] is a
NULL`指针,并尝试取消引用它会导致分段错误。或者下一页内存不可读或写,尝试访问它会导致程序出现保护错误。或者实现边界 - 在运行时检查您的数组访问并使程序崩溃。也许运行时在数组之后粘贴了一个canary值,然后检查它是否被覆盖。或者偶然发生这种情况。
实际上,您真的希望编译器能够为您捕获这些错误,而不是试图追踪缓冲区溢出导致的错误。使用std::vector
而不是动态数组会更好。如果你必须使用数组,你想要检查你所有的访问都是自己的,因为你不能依赖编译器为你做这些并跳过它们是导致错误的主要原因。
答案 1 :(得分:2)
如果您在使用new创建的数组或其他对象的末尾之外进行编写或读取,则C ++标准不再定义您的程序行为。
任何事情都可能发生,编译器和程序仍然符合标准。
在这种情况下最可能发生的事情是你正在破坏堆中的内存。在一个小程序中,这个&#34;似乎工作&#34;因为堆ypu使用的部分并没有被任何其他代码使用,所以在较大的部分中,你会崩溃或在一个看似无关的代码中随机行为。
但任意事情都可能发生。编译器可以证明分支导致超出数组末尾的访问,并且死代码消除了导致它的路径(UB随时间移动),或者它可能会遇到受保护的内存区域并崩溃,或者它可能破坏堆管理数据并导致未来的新/删除崩溃,或鼻子恶魔,或其他任何东西。
答案 2 :(得分:1)
在for
循环中,您正在分配超出循环边界的元素,并记住C ++不进行边界检查。
因此,当您初始化数组时,您正在初始化超出数组的范围(假设用户输入3
length
,您正在初始化1到array[0]
到array[5]
因为条件是n <= length + 1
;
当你超出它的范围时,数组的行为是不可预测的,但很可能你的程序会崩溃。在这种情况下,您将使用超出其边界的2个元素,因为您在条件和=
中使用了length + 1
。
答案 3 :(得分:1)
不要求new []
运算符分配的内存多于请求的内存。
发生的事情是您的代码在分配的数组的末尾运行。因此它具有不确定的行为。
未定义的行为意味着C ++标准对发生的事情没有要求。因此,如果您的程序SEEMS正常工作(在您的情况下),您的实现(在本例中为编译器和标准库)将同样正确,产生运行时错误,破坏您的系统驱动器或其他任何东西。< / p>
实际上,所有发生的事情都是你的代码写入内存,然后从内存中读取,超过分配的内存块的末尾。发生什么取决于该内存位置的实际内容。在您的情况下,无论在该内存位置发生什么,都可以修改(在循环中)或读取(以便打印到std::cout
)。
结论:解释不是new[]
过度分配。这是你的代码有不确定的行为,所以无论如何似乎都可以工作。