对于以下代码:
foo(int n){
int array[n];
}
Run Code Online (Sandbox Code Playgroud)
我理解这是无效的语法,并且它是无效的,因为c ++标准要求在编译时设置数组大小(尽管一些编译器支持以下语法).
但是我也理解以下是有效的语法:
bar(int n){
int *array = new int[n];
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么这是允许的,是不是创建一个在运行时确定大小的数组?这样做是好的做法,还是应该使用向量,如果我需要这样做呢?
Ste*_*ppo 26
那是因为前者在堆栈上分配而后者在堆上分配.
当您在堆栈上分配内容时,了解对象的大小对于正确构建它是必不可少的.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]};
Run Code Online (Sandbox Code Playgroud)
在表达式中
new int[n]
Run Code Online (Sandbox Code Playgroud)
int[n]
不是类型。C++ 以不同的方式对待“new
带数组”和“new
带非数组”。N3337 标准草案是这样说的new
:
当分配的对象是一个数组时(即使用noptr-new-declarator语法或new-type-id或type-id表示数组类型),new-expression产生一个指向初始元素的指针(如果任何) 数组。
该noptr新说明符是指这种特殊情况下(评估n
和创建这个尺寸的阵列),请参阅:
noptr-new-declarator:
[表达式] 属性说明符-seq opt
noptr-new-declarator [常量表达式] 属性说明符-seq opt
但是,您不能在“通常”声明中使用它,例如
int array[n];
Run Code Online (Sandbox Code Playgroud)
或在 typedef
typedef int variable_array[n];
Run Code Online (Sandbox Code Playgroud)
这与 C99 VLA 不同,C99 VLA 两者都是允许的。
我应该改用向量吗?
是的你应该。你应该一直使用向量,除非你有非常强烈的理由不这样做(在过去的 7 年里,我曾经使用过一次new
——当我在vector
完成一项学校作业时)。