为什么 new int[n] 有效,而 int array[n] 无效
Why is new int[n] valid when int array[n] is not?
对于以下代码:
foo(int n){
int array[n];
}
我知道这是无效的语法,并且它是无效的,因为 c++ 标准要求在编译时设置数组大小(尽管某些编译器支持以下语法(。
但是,我也了解以下语法是有效的语法:
bar(int n){
int *array = new int[n];
}
我不明白为什么允许这样做,这与创建在运行时确定大小的数组不同吗?这样做是很好的做法,还是如果需要这样做,我应该使用矢量?
这是因为前者在堆栈上分配,后者在堆上分配。
当您在堆栈上分配某些内容时,了解对象的大小对于正确构建它至关重要。C99 允许在运行时指定大小,这在构建和拆除上述堆栈时引入了一些复杂性,因为您无法在编译时计算其大小。必须发出机器代码才能在程序执行期间执行上述计算。这可能是此功能未包含在C++标准中的主要原因。
相反,顾名思义,堆没有固定的结构。任何大小的块都可以以没有特定顺序进行分配,只要它们不重叠并且您有足够的(虚拟(内存¹。在这种情况下,在编译时知道大小并不那么重要。
另外,请记住,堆栈的大小有限,主要用于在无限递归消耗所有可用内存之前检测它们。通常限制固定在 1MB 左右,您很少达到这个限制。除非您分配大型对象,否则这些对象应放置在堆中。
至于你应该使用什么,可能是一个std::vector<int>
.但这真的取决于你想做什么。
另请注意,C++11 有一个 std::array
类,必须在编译时知道其大小。C++14 应该引入 std::dynarray
,但它被推迟了,因为关于编译时未知大小的堆栈分配还有很多工作要做。
¹ 块通常出于性能原因按顺序分配,但这不是必需的。
² 如前所述,在编译时知道大小并不是一个硬性要求,但它使事情变得更简单。
在第一种情况下,您静态分配内存空间来保存整数。这是在编译程序时完成的,因此存储量不灵活。
在后一种情况下,您将动态分配内存空间来保存整数。这是在程序运行时完成的,因此所需的存储量可以灵活。
第二个调用实际上是一个与操作系统对话的函数,以便在内存中找到要使用的位置。在第一种情况下不会发生相同的过程。
int array[n]
在编译时在调用堆栈上分配一个固定长度的数组,因此n
需要在编译时知道(除非使用特定于编译器的扩展来允许在运行时分配,但数组仍在堆栈上(。
int *array = new int[n]
运行时在堆上分配一个动态长度数组,因此n
不需要在编译时知道
你的问题唯一有效的答案是,因为标准是这样说的。
与C99相比,C++从不费心指定可变长度数组(VLA(,因此获得可变大小数组的唯一方法是使用动态分配,malloc
,new
或其他内存管理器。
公平地说,C++,运行时大小的堆栈分配使堆栈展开稍微复杂,这也会使使用该功能的函数的异常处理更加麻烦。
无论如何,即使您的编译器提供了 C99 功能作为扩展,最好始终严格控制堆栈的使用:
没有办法从吹垮堆栈限制中恢复,并且由于某种原因,错误情况只是保留为未定义行为。
在C++中模拟VLA的最简单方法,尽管没有避免动态分配(以及突破极限的危险(的性能优势:
unique_ptr<T[]> array{new T[n]};
在表达式中
new int[n]
int[n]
不是类型。C++对待"数组new
"和"非数组new
"不同。N3337标准草案对new
有这样的说法:
当分配的对象是数组(即,使用 noptr-new-declarator 语法或 new-type-id 或 type-id 表示数组类型(时,new-expression 将生成指向数组的初始元素(如果有(的指针。
noptr-new-declarator 引用了这种特殊情况(评估n
并创建此大小的数组(,请参阅:
noptr-new-declarator:
[ 表达式 ] 属性-说明符-序列选项
noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
但是,您不能在"通常"声明中使用它,例如
int array[n];
或在typedef
typedef int variable_array[n];
这与 C99 VLA 不同,在 C99 VLA 中,两者都是允许的。
我应该改用向量吗?
是的,你应该。你应该一直使用向量,除非你有非常充分的理由不这样做(在过去的 7 年里,有一次我使用了new
- 当我为学校作业实施 vector
时(。
不,第二个不是声明数组。 它使用数组形式 operator new
,并且特别允许第一维是可变的。
这是因为C++语言没有C99中引入的称为"可变长度数组"(VLA(的C功能。
C++在采用此 C 功能方面滞后,因为其库中的 std::vector
类型满足了大多数要求。
此外,2011年的C标准倒退了,使VLA成为可选功能。
简而言之,VLA 允许您使用运行时值来确定在自动存储中分配的本地数组的大小:
int func(int variable)
{
long array[variable]; // VLA feature
// loop over array
for (size_t i = 0; i < sizeof array / sizeof array[0]; i++) {
// the above sizeof is also a VLA feature: it yields the dynamic
// size of the array, and so is not a compile-time constant,
// unlike other uses of sizeof!
}
}
VLA早在C99之前就存在于GNU C方言中。在没有 VLA 的 C 方言中,声明中的数组维度必须是常量表达式。
即使在带有VLA的C方言中,也只有某些数组可以是VLA。例如,静态数组不能,动态数组也不能(对于结构内的实例数组,即使该结构的实例是动态分配的(。
无论如何,由于您正在C++编码,因此这是没有意义的!
请注意,使用 operator new
分配的存储不是 VLA 功能。这是动态分配的特殊C++语法,它返回指针类型,如您所知:
int *p = new int[variable];
与VLA不同,这个对象将一直存在,直到它被delete []
显式销毁,并且可以从周围的范围返回。
有不同的语义:
如果n
是编译时常量(与您的示例不同(:
int array[n]; //valid iff n is compile-time constant, space known at compile-time
但请考虑何时n
是运行时值:
int array[n]; //Cannot have a static array with a runtime value in C++
int * array = new int[n]; //This works because it happens at run-time,
// not at compile-time! Different semantics, similar syntax.
在 C99 中,您可以为数组设置运行时n
,并在运行时在堆栈中创建空间。C++中有一些类似的扩展建议,但还没有一个进入标准。
您可以在堆栈上静态分配内存,也可以在堆上动态分配内存。
在第一种情况下,您的函数包含具有可能可变长度的数组的声明,但这是不可能的,因为原始数组在编译时必须具有固定大小,因为它们是在堆栈上分配的。因此,它们的大小必须指定为常量,例如 5
.你可以有这样的东西:
foo(){
int array[5]; // raw array with fixed size 5
}
使用指针,您可以为将指向的内存指定可变大小,因为此内存将在堆上动态分配。在第二种情况下,您将使用参数 n
来指定将分配的内存空间。
最后,我们可以说指针不是数组:使用指针分配的内存在堆上分配,而为原始数组分配的内存在堆栈上分配。
原始数组有很好的替代品,例如标准容器向量,它基本上是一个长度大小可变的容器。
确保您很好地理解动态和静态内存分配之间的区别,堆栈上分配的内存和堆上分配的内存之间的差异。
- 如何从map<pair<string,int>,pair<string,Array>>中迭代和查找?
- 如何将 int[4] 转换为 std::array<int,4>?
- 是否有可能在没有复制的情况下传递 std::vector<int> 作为参数来获得 std::vector<std::array<int, 3>>?
- 尝试使用 Array 类的模板构造整数数组时,错误:为什么数组类型"int [5]"不可分配,
- 如何就地构造 std::map<int, std::array<Class, 2>>?
- 如何从方法返回静态常量 int std::array?
- 尝试使用 const int std::array 时出错
- Visual Studio 不采用 array: int magicSquare[n][n]?
- 混淆从"array of int[]"到"pointer to int"的显式衰减?
- 为什么我们不能将 int array[] 传递给 hoo(int* &p)?
- "Converting" 从 int[] 到 std::array at fread?
- Int Array[]打印不好
- 从 int 转换为 std::array<unsigned char, 1ul>::value_type 可能会改变其值
- 为什么 new int[n] 有效,而 int array[n] 无效
- 带有参数的 func return list (int arry[int]<int>, int array[int])
- iPhone SDK 2d int array
- 为什么是 int array[][20]
- int * array[60] 和 int * array = new int(60);之间的区别
- Int *array = new Int [n];这个函数到底在做什么
- 错误 LNK2001:未解析的外部符号"int * array"