使用new创建数组而不声明大小
Creating an array using new without declaring size
这已经困扰我很长一段时间了。我有一个指针。我声明一个类型为int.的数组
int* data;
data = new int[5];
我相信这会创建一个大小为5的int数组。因此,我将能够存储从data[0]到data[4]的值。
现在我以同样的方式创建一个数组,但没有大小。
int* data;
data = new int;
我仍然能够将值存储在数据[2]或数据[3]中。但我创建了一个大小为1的数组。这怎么可能?
我知道数据是指向数组第一个元素的指针。虽然我还没有为接下来的元素分配内存,但我仍然可以访问它们。怎样
谢谢。
通常,不需要使用new
"手动"分配数组。使用CCD_ 2更方便,也更安全。并将动态内存管理的正确实现留给标准库的作者。
std::vector<int>
可选地通过at()
方法为元素访问提供边界检查。
示例:
#include <vector>
int main() {
// create resizable array of integers and resize as desired
std::vector<int> data;
data.resize(5);
// element access without bounds checking
data[3] = 10;
// optionally: element access with bounds checking
// attempts to access out-of-range elements trigger runtime exception
data.at(10) = 0;
}
C++中的默认模式通常是允许用未定义的行为射中自己的脚,正如您在案例中看到的那样。
供参考:
- https://en.cppreference.com/w/cpp/container/vector
- https://en.cppreference.com/w/cpp/container/vector/at
- https://en.cppreference.com/w/cpp/language/ub
- 未定义、未指定和实现定义的行为
- C++程序员应该知道哪些常见的未定义行为
此外,在第二种情况下,您根本不分配数组,而是分配一个对象。请注意,您也必须使用匹配的delete
运算符。
int main() {
// allocate and deallocate an array
int *arr = new int[5];
delete[] arr;
// allocate and deallocate a single object
int *p = new int;
delete p;
}
供参考:
- https://en.cppreference.com/w/cpp/language/new
- https://en.cppreference.com/w/cpp/language/delete
- delete[]是如何知道的';是数组吗
使用new int
时,访问data[i]
,其中i!=0
具有未定义的行为。但这并不意味着手术会立即失败(或每次失败,甚至永远失败)。在大多数体系结构中,您所要求的块末尾之外的内存地址很可能被映射到您的进程,并且您可以访问它们。如果你不写信给他们,你可以访问他们也就不足为奇了(尽管你不应该)。即使你向它们写入,大多数内存分配器都有一个最小的分配,在幕后,你很可能已经被分配了更多(4是现实的)整数的空间,即使代码只请求1。您也可能会覆盖某些内存区域,但永远不会被绊倒。在数组末尾以外写入的一个常见后果是损坏可用内存存储本身。其后果可能是灾难,但可能只会在稍后分配类似大小的对象时表现出来。
依赖这种行为是一个可怕的想法,但它似乎起作用也就不足为奇了。C++(通常或默认情况下)不执行严格的范围检查,访问无效的数组元素可能有效,或者至少在最初看起来有效。
这就是为什么C和C++会受到奇怪和间歇性错误的困扰。并非所有引发未定义行为的代码在每次执行中都会灾难性地失败。
在C++中超出数组的边界是未定义的行为,因此任何事情都可能发生,包括看起来"正确"工作的事情。
在常见系统的实际实现术语中,您可以将"虚拟"内存视为一个从0到指针大小的大"平面"空间,指针位于该空间中。
进程的"虚拟"内存被映射到物理内存、页面文件等。现在,如果您访问未映射的地址,或者试图写入只读部分,则会出现错误,例如访问冲突或segfault。
但为了提高效率,这种映射是针对相当大的块进行的,例如针对4KiB"页面"。进程中的分配器,如new
和delete
(或堆栈),将根据需要进一步拆分这些页面。因此,访问有效页面的其他部分不太可能引发错误。
这带来了一个不幸的结果,即很难检测到这种越界访问、释放后使用等。在许多情况下,写入会成功,只会损坏其他看似无关的对象,这可能会导致稍后的崩溃或错误的程序输出,因此最好非常小心C和C++内存管理。
data = new int; // will be some virtual address
data[1000] = 5; // possibly the start of a 4K page potentially allowing a great deal beyond it
other_int = new int[5];
other_int[10] = 10;
data[10000] = 42; // with further pages beyond, so you can really make a mess of your programs memory
other_int[10] == 42; // perfectly possible to overwrite other things in unexpected ways
C++提供了许多工具来提供帮助,如std::string
、std::vector
和std::unique_ptr
,通常最好完全避免手动new
和delete
。
new int
只分配1个整数。如果访问的偏移量大于0,例如data[1]
,则会覆盖内存。
int *
是指向可能是int
的东西的指针。当您使用std::vector<int>
0进行分配时,您将分配一个int并将地址存储到指针。实际上,int *
只是一个指向某些内存的指针。
我们可以将int *
视为指向标量元素(即new int)或元素数组的指针——语言无法告诉您指针真正指向的是什么;停止使用指针并仅使用标量值和CCD_ 23是一个非常好的参数。
当您说a[2]
时,您很好地访问了a
所指向的值之后的内存sizeof(int)
。如果a
指向一个标量值,那么a
之后的任何内容都可能导致未定义的行为(您的程序实际上可能会崩溃——这是一个实际的风险)。写信给那个地址最容易引起问题;这不仅是一种风险,而且是您应该积极防范的风险——例如,如果您需要数组,请使用std::vector
,如果您不需要,请使用int
或int&
。
表达式a[b]
是另一种写入*(a+b)
的方法,其中一个操作数是指针。为了保持理智,我们假设a
是这里的指针(但由于加法是交换的,它可以是另一种方式!试试看!);则a
中的地址增加b
乘以sizeof(*a)
,得到*a
之后的第b
个对象的地址。
结果指针被取消引用,从而为地址为a+b
的对象生成一个"名称"。
注意,a
不必是一个数组;如果它是一,则在应用运算符[]
之前,它"衰减"到一个指针。操作发生在键入的指针上如果该指针无效,或者a+b
处的内存实际上没有保存*a
类型的对象,或者即使该对象与*a
无关(例如,因为它不在同一数组或结构中),则行为是未定义的。
在现实世界中,"普通"程序不进行任何边界检查,只是简单地将偏移量添加到指针并访问该内存位置。(访问越界内存当然是C和C++中更常见的错误之一,也是这些语言并非没有限制的原因之一,建议用于高安全性应用程序。)
如果索引b
很小,那么您的程序可能可以访问内存。对于像int
这样的普通旧数据,最有可能的结果是您只需读取或写入该位置的内存。这就是你的遭遇。
由于覆盖了不相关的数据(实际上可能被程序中的其他变量使用),因此在更复杂的程序中,结果往往令人惊讶。这样的错误可能很难找到,而且有一些工具可以检测这种越界访问。
对于较大的索引,您将在某个时刻进入未分配给程序的内存,这将导致Windows NT及以上现代系统立即崩溃,并且在没有内存管理的体系结构上产生不可预测的结果。
我仍然可以将值存储在data[2]或data[3]中。但我创建了一个大小为1的数组。这怎么可能?
程序的行为未定义。
此外,您没有创建大小为1的数组,而是创建了一个非数组对象。差别是微妙的。
- .cpp和.h文件中的模板专用化声明
- 未在作用域中声明unordered_map
- C++避免重复声明的语法是什么
- 如何确保C++函数在定义之前声明(如override关键字)
- 错误:未在此范围内声明'reverse'
- 奇怪的(对我来说)返回声明 - 在谷歌上找不到任何关于它的信息
- 在类c++中使用new声明数组
- 声明C++数组(带或不带 "new" 关键字)
- 使用new创建数组而不声明大小
- 为什么当我使用"new"关键字声明数组指针时无法删除它
- 将函数放在类中,然后声明"New"是否使其线程安全?
- 当声明成员函数时,*New()是什么意思
- 访问用new声明的数据的正确方式
- 如何在c++中使用new声明多个2d数组
- 双指针二维数组声明int** seq=new int* [n];
- 在函数中声明new int[]时,在main中使用delete[]
- 变量声明冲突导致编译器错误:"conflict with new declaration with '
- 替换函数'operator new'不能声明'inline' [-werror,-winline-new-delete]
- 直接声明变量和使用关键字new有什么区别
- 使用 'new int[n]' 声明新数组时,为什么它没有出现在局部变量列表中?