【问题标题】:C++ array sizes without constant expressions没有常量表达式的 C++ 数组大小
【发布时间】:2016-03-23 12:43:11
【问题描述】:

我正在阅读 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]合法呢?

【问题讨论】:

  • 栈上的数组需要常量表达式,而不是堆上的。 learncpp.com/cpp-tutorial/79-the-stack-and-the-heap
  • 段落的其余部分暗示了第 9 页的引用是指数组的 声明
  • @GillBates 它与数组的存储位置无关。如果数组是类的成员并且该类的实例是动态分配的,则数组声明仍然需要常量表达式大小,尽管实际数组“在堆上”。
  • 作者还称这本书很“薄”。在我看来,这不是一本可以从中学习这些基本概念的书。
  • @molbdnilo 这显然不是我的意思。我说的是数组本身是如何初始化的。通过new 使用指向它的指针或只是在本地。

标签: c++ arrays


【解决方案1】:

已分配数组(即使用new[] 表达式创建的数组,如new double[s])之间存在区别,其生命周期必须由代码管理(通过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&lt;int,5&gt;)或不在堆栈上(例如static double x[100];

使用 已分配 数组,大小不必是常量表达式。大小将简单地编码到分配器以某种方式产生的内存块中(例如实际数据开始之前的四个前导字节),以便相应的delete[] 知道要删除多少元素。

对于已声明数组(或未分配数组,没有new/malloc/等),大小必须编码到类型中,以便析构函数知道该做什么。唯一允许的标准数组声明是:

T D[constant-expression_opt];

(其中D 是一个声明符,可以是名称或另一个数组声明等)声明的数组不限于堆栈。请注意,为了增加混淆,常量表达式是可选的。

数组在 C++ 中提供了许多混淆来源。分配和声明的数组具有不同的大小规则和不同的管理实践,但您可以将T* 分配给其中任何一个,并且它们被等效索引。分配的数组一个指针(这就是你得到的全部),但声明的数组衰减指向一个指针(但一个数组!)。


请注意,一个可变长度数组 (VLA) 的概念。例如,gcc 支持它们作为扩展,但它们是非标准 C++。不过,它会定期提出,您可以查看this question 了解有关它们的更多信息。

【讨论】:

  • 比目前最受好评的答案要好得多。
  • 也许您还可以添加有关sizeof 运算符行为的信息(例如sizeof(p) == sizeof(int *)sizeof(q) == 10*sizeof(int)
  • @king_nak 我不认为这是添加额外信息,因为类型已经列出。重要的是类型。
  • 这是分配和声明数组的另一个区别属性。初学者经常尝试使用sizeof(arr) 来获取已分配数组中的元素数量,但这不会给出正确的结果。我只是觉得值得一提
  • @king_nak 我会说类型是 the 区别属性。大小是类型的函数。
【解决方案2】:

当创建一个由编译器管理其内存的数组时,它的大小必须是(编译时)常量。例如:

int a[5];
const int sz = 7;
int b[sz] = {0};

(某些语言例如:C(C99 及以上)支持动态数组大小)

如果您想要一个动态大小的数组(您提供的示例 sn-p),您需要自己为其分配内存,您还需要在完成后使用 delete 释放它。这种数组的大小也可以是非常量的。此外,您需要自己管理内存,分配可能会失败,并且运算符(例如sizeof)将在指针而不是数组上进行操作。

在现代 C++(C++11 及更高版本)中,即使 stl 容器 std::array 也必须具有恒定大小。

【讨论】:

    【解决方案3】:

    报价

    数组的大小必须是常量表达式。

    说的是数组声明,比如

    double a[EXPR];
    

    其中EXPR 必须确实是一个常量或 constexpr(C 具有可变长度数组,但它们不是标准 C++ 的一部分)。

    你提到的反例表达,

    new double[s]
    

    不是数组,尽管有[]。它是一个new-expression,并产生一个指针,而不是一个数组。您没有显示v.elem 的定义,但我可以看出它是一个指向双精度的指针。

    关于新表达式的链接讨论中的注释

    如果 type 是数组类型,则除第一个维度之外的所有维度都必须指定为正数{类似于整数常量 - 省略细节}。

    所以上面提到的typedouble[s],这是明确允许的。

    诚然,数组和 传递给新表达式的数组类型之间的区别有点微妙,但你不能仅仅因为 [] 就将它们混为一谈,就像你一样可以声称

    map["key"]
    

    声明一个长度为"key"的数组违反了某些规定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 2010-09-14
      相关资源
      最近更新 更多