C++ new[] 运算符创建长度 = 长度 + 1 的数组?
C++ new[] operator creates array of length = length + 1?
为什么 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 处分配一个值。如果我们尝试做 length+2,我们会得到一个错误。
这是为什么?为什么C++让length = length + 1?
在 for
循环中,您正在为超出循环边界的元素赋值,请记住 C++ 不进行边界检查。
因此,当您初始化数组时,您正在初始化数组的边界(假设用户输入 3
表示 length
您正在将 1 初始化为 array[0]
到 array[5]
因为条件是 n <= length + 1
;
当您超出数组的范围时,数组的行为是不可预测的,但您的程序很可能会崩溃。在这种情况下,您将超出其范围的 2 个元素,因为您在条件中使用了 =
和 length + 1
.
没有。您可以计算 地址 array + n
,以检查另一个地址是否小于它。尝试访问元素 array[n]
是 未定义的行为 ,这意味着程序变得毫无意义,编译器可以做任何事情。从字面上看任何东西;一个旧版本的 GCC,如果它看到 #pragma
指令,就会在终端上启动一个 roguelike 游戏。 (谢谢,Revolver_Ocelot,提醒我:这在技术上是 implementation-defined 行为,一个不同的类别。)即使计算地址 array + n + 1
也是 未定义的行为 .
因为它可以做任何事情,所以您尝试使用它的特定编译器决定让您搬起石头砸自己的脚。例如,如果数组后的下两个词是堆中另一个块的 header,您可能会遇到 memory-corruption 错误。或者编译器可能将数组存储在内存 space 的顶部,地址 &array[n+1] is a
NULL` 指针,并试图取消引用它会导致分段错误。或者下一页内存不可读或不可写,试图访问它会使程序因保护错误而崩溃。或者可能 bounds-checks 你的数组在运行时访问并导致程序崩溃。也许运行时在数组后插入了一个金丝雀值,稍后检查它是否被覆盖。也可能是偶然发生的工作。
在实践中,您确实希望编译器为您捕获这些错误,而不是稍后尝试追踪缓冲区溢出导致的错误。使用 std::vector
比动态数组更好。如果你必须使用一个数组,你想检查你所有的访问都是in-bounds你自己,因为你不能依赖编译器为你做那件事并且跳过它们是一个错误的主要原因。
如果您写入或读取超出数组末尾或使用 new 创建的其他对象,您的程序的行为将不再由 C++ 标准定义。
任何事情都有可能发生,编译器和程序仍然符合标准。
在这种情况下最有可能发生的事情是您破坏了堆中的内存。在一个小程序中,这个 "seems to work" 作为堆 ypu 使用的部分没有被任何其他代码使用,在一个更大的程序中,你会崩溃或在看似无关的代码的其他地方随机运行。
但任意事情都可能发生。编译器可以证明一个分支导致访问超出数组的 tue 末尾,并且死代码消除了导致它的路径(时间旅行的 UB),或者它可能到达受保护的内存区域并崩溃,或者它可能破坏堆管理数据并导致未来 new/delete 崩溃,或鼻恶魔,或其他任何东西。
不要求 new []
运算符分配比请求更多的内存。
发生的事情是您的代码 运行ning 超出了分配数组的末尾。因此它具有未定义的行为。
未定义的行为意味着 C++ 标准对发生的事情没有强加任何要求。因此,如果您的程序 SEEMS 正常工作(就像您的情况一样)、产生 运行 时间错误、破坏您的系统驱动器,或者还有什么。
实际上,所有发生的事情是您的代码正在写入内存,然后从该内存中读取,超过分配的内存块的末尾。发生什么取决于该内存位置中实际存在的内容。在您的情况下,该内存位置中的任何内容都可以修改(在循环中)或读取(以便打印到 std::cout
)。
结论:解释不是new[]
过度分配。这是因为您的代码具有未定义的行为,因此似乎无论如何都可以工作。
为什么 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 处分配一个值。如果我们尝试做 length+2,我们会得到一个错误。
这是为什么?为什么C++让length = length + 1?
在 for
循环中,您正在为超出循环边界的元素赋值,请记住 C++ 不进行边界检查。
因此,当您初始化数组时,您正在初始化数组的边界(假设用户输入 3
表示 length
您正在将 1 初始化为 array[0]
到 array[5]
因为条件是 n <= length + 1
;
当您超出数组的范围时,数组的行为是不可预测的,但您的程序很可能会崩溃。在这种情况下,您将超出其范围的 2 个元素,因为您在条件中使用了 =
和 length + 1
.
没有。您可以计算 地址 array + n
,以检查另一个地址是否小于它。尝试访问元素 array[n]
是 未定义的行为 ,这意味着程序变得毫无意义,编译器可以做任何事情。从字面上看任何东西;一个旧版本的 GCC,如果它看到 #pragma
指令,就会在终端上启动一个 roguelike 游戏。 (谢谢,Revolver_Ocelot,提醒我:这在技术上是 implementation-defined 行为,一个不同的类别。)即使计算地址 array + n + 1
也是 未定义的行为 .
因为它可以做任何事情,所以您尝试使用它的特定编译器决定让您搬起石头砸自己的脚。例如,如果数组后的下两个词是堆中另一个块的 header,您可能会遇到 memory-corruption 错误。或者编译器可能将数组存储在内存 space 的顶部,地址 &array[n+1] is a
NULL` 指针,并试图取消引用它会导致分段错误。或者下一页内存不可读或不可写,试图访问它会使程序因保护错误而崩溃。或者可能 bounds-checks 你的数组在运行时访问并导致程序崩溃。也许运行时在数组后插入了一个金丝雀值,稍后检查它是否被覆盖。也可能是偶然发生的工作。
在实践中,您确实希望编译器为您捕获这些错误,而不是稍后尝试追踪缓冲区溢出导致的错误。使用 std::vector
比动态数组更好。如果你必须使用一个数组,你想检查你所有的访问都是in-bounds你自己,因为你不能依赖编译器为你做那件事并且跳过它们是一个错误的主要原因。
如果您写入或读取超出数组末尾或使用 new 创建的其他对象,您的程序的行为将不再由 C++ 标准定义。
任何事情都有可能发生,编译器和程序仍然符合标准。
在这种情况下最有可能发生的事情是您破坏了堆中的内存。在一个小程序中,这个 "seems to work" 作为堆 ypu 使用的部分没有被任何其他代码使用,在一个更大的程序中,你会崩溃或在看似无关的代码的其他地方随机运行。
但任意事情都可能发生。编译器可以证明一个分支导致访问超出数组的 tue 末尾,并且死代码消除了导致它的路径(时间旅行的 UB),或者它可能到达受保护的内存区域并崩溃,或者它可能破坏堆管理数据并导致未来 new/delete 崩溃,或鼻恶魔,或其他任何东西。
不要求 new []
运算符分配比请求更多的内存。
发生的事情是您的代码 运行ning 超出了分配数组的末尾。因此它具有未定义的行为。
未定义的行为意味着 C++ 标准对发生的事情没有强加任何要求。因此,如果您的程序 SEEMS 正常工作(就像您的情况一样)、产生 运行 时间错误、破坏您的系统驱动器,或者还有什么。
实际上,所有发生的事情是您的代码正在写入内存,然后从该内存中读取,超过分配的内存块的末尾。发生什么取决于该内存位置中实际存在的内容。在您的情况下,该内存位置中的任何内容都可以修改(在循环中)或读取(以便打印到 std::cout
)。
结论:解释不是new[]
过度分配。这是因为您的代码具有未定义的行为,因此似乎无论如何都可以工作。