我正在阅读Stroustrup's C ++之旅。在第9页,他说:
"数组的大小必须是常量表达式。"
然而,后来,pg。 16,他使用以下代码示例:
void vector vector_init(Vector& v, int s)
{
v.elem = new double[s]; // Allocate an array of s doubles
v.sz = s;
}
此处s
不是常量表达式,因此如何将v.elem
初始化为new double[s]
合法?
答案 0 :(得分:39)
分配的数组(即使用new[]
表达式创建的数组(如new double[s]
)之间存在差异,其生命周期必须由代码管理(via {{ 1}})和声明数组,其生命周期仅由其范围管理:
delete[]
区别不是基于堆栈/堆。声明的数组可以堆栈分配(例如int* p = new int[s]; // allocated array, p has type int*
int q[10]; // declared array, q has type int[10]
std::vector<int> u; // has member allocated array
std::array<int, 5> v; // has member declared array
)或不在堆栈上(例如new array<int,5>
)
使用分配的数组,大小不必是常量表达式。大小将被简单地编码到分配器以某种方式产生的存储器块中(例如,在实际数据开始之前的四个前导字节),以便相应的static double x[100];
知道要删除多少元素。
使用声明的数组(或未分配的数组,没有delete[]
/ new
/ etc。),大小必须†为编码到类型中,以便析构函数知道该怎么做。唯一允许的标准数组声明是:
malloc
(其中T D[constant-expression_opt];
是一个可以是名称或其他数组声明的声明符,等等。)声明的数组不限于堆栈。请注意,为了增加混淆,constant-expression是可选的。
数组在C ++中提供了许多混淆源。分配和声明的数组具有不同的大小规则和不同的管理实践,但您可以为其中任何一个分配D
,并且它们等效地编入索引。分配的数组是一个指针(你得到的全部),但声明的数组衰减指针(但是一个阵!)。
†请注意,是可变长度数组(VLA)的概念。例如,gcc支持它们作为扩展,但它们是非标准的C ++。它会定期提出,您可以看到this question以获取有关它们的更多信息。
答案 1 :(得分:11)
创建内存由编译器管理的数组时,其大小必须为(编译时)常量。例如:
int a[5];
const int sz = 7;
int b[sz] = {0};
(例如,某些语言:C(C99以后)支持动态数组大小)
如果你想要一个动态大小的数组(由你给出的示例片段),你需要自己为它分配内存,你还需要在完成后用delete
释放它。这种数组的大小也可以是非const的。此外,您需要自己管理内存,分配可能会失败,运算符(例如sizeof
)将对指针而不是数组进行操作。
在现代C ++(C ++ 11及更高版本)中,即使是stl容器std::array
也必须具有恒定的大小。
答案 2 :(得分:4)
报价
数组的大小必须是常量表达式。
正在讨论数组声明,例如
double a[EXPR];
其中EXPR
必须确实是常量或constexpr(C具有可变长度数组,但它们不是标准C ++的一部分)。
你提到的表达式作为反例,
new double[s]
不是数组,尽管[]
。它是new-expression,产生指针,而不是数组。你没有显示v.elem
的定义,但我可以告诉它是指向双倍的指针。
关于新表达式
的链接讨论如果type是数组类型,则必须将除第一个之外的所有维度指定为正{类似于整数常量 - 详细信息}。
所以上面提到的类型是double[s]
,这是明确允许的。
不可否认,数组和传递给新表达式的数组类型之间的区别有点微妙,但你不能因为[]
而将它们混为一谈比你声称的那样
map["key"]
通过声明长度为"key"
的数组来违反某些内容。