【问题标题】:The array is static, but the array size isn't know until runtime. How is this possible?数组是静态的,但数组大小直到运行时才知道。这怎么可能?
【发布时间】:2012-02-10 07:08:46
【问题描述】:

这让我困扰了一段时间。它触及了我(缺乏)对静态和动态内存分配之间差异的理解的核心。下面的数组是一个普通的静态数组,应该是编译时分配内存的吧?然而,我已经设置好让用户在运行时输入数组大小。

#include <iostream>
using namespace std;

int main() {
  cout << "how many elements should the array hold? ";
  int arraySize;
  cin >> arraySize;

  int arr[arraySize];

  for (int i = 0; i < arraySize; ++i)
    arr[i] = i * 2;

  return 0;
}

请注意,此程序中没有 newdelete 运算符。它在 Xcode 4.2(默认 Clang 编译器)以及我学校的 UNIX 服务器(GCC 4.4.5)中运行良好。在编译时创建数组时,编译器如何知道要为arr 分配多少内存?这只是我的编译器的侥幸,可能破坏其他内存的危险代码,还是合法的?

【问题讨论】:

  • 这使用了一个名为可变长度数组的功能,该功能在 C99 中首次亮相。
  • 尝试用g++ -Wall -Wextra -pedantic -std=c++98编译

标签: c++ arrays dynamic-memory-allocation dynamic-arrays


【解决方案1】:

这是 C++ 编译器的非标准扩展。请注意,在 C 中,与 C++ 不同,这是自 C99 以来官方支持的(即标准强制行为)。在 C++ 中,它不受支持,因为已经有解决该问题的方法:使用 std::vector 而不是数组。

然而数组不是不是使用静态内存分配(也不是动态内存分配),而是自动内存分配。自动变量在函数结束时自动释放(分配它们的内存区域称为堆栈,因为其上的分配和释放具有堆栈语义)。要让数组使用静态内存分配,您必须将 static 放在定义前面(请注意,全局或命名空间范围内的变量始终使用静态内存分配)。但是,如果您将变量设为静态,您会发现编译器不再允许使用非常量数组大小。

请注意,std::vector 使用动态内存分配存储其数据。因此,即使是静态std::vectors,您也可以使用非常量大小。

【讨论】:

  • 不允许的主要原因是没有vector的替代品。有更深层次的原因,因为数组的大小是类型的一部分,并且必须在编译时知道。
  • @DavidRodríguez-dribeas:他们本可以对这些规则进行例外处理,就像 C 可变长度数组需要 C 规则的例外一样。
【解决方案2】:

这是一个Variable Length Array(仅在 C99 中支持,在 C++ 中不支持)。它在运行时分配在堆栈上。

【讨论】:

  • 这甚至是有效的 C++ 吗? IIRC Visual Studio 对此有一些错误。
  • 不,这不是动态内存分配。这是自动内存分配。 @stativ:不,它不是有效的 C++(但是,如果没有被 C++ 特定代码包围,它将是有效的 C)。对于 C++,它是一些编译器的非标准扩展。
  • @celtschk:有没有要求在堆栈上分配内存?如果一个特定的实现能够在堆栈上分配它,那可能比分配堆内存然后释放它更有效,但是是否有任何理由不允许使用不能使用堆栈的实现而不是堆?
  • @supercat:不要求它在 stack 上(或者首先要有一个堆栈)。但是有一个要求是自动内存分配(即离开函数作用域时自动回收的内存),而不是动态内存分配(即内存必须通过调用内存释放函数手动释放)。我不确定是否允许通过编译器生成的代码调用malloc/free
【解决方案3】:

对于在函数内部声明的数组(或任何对象),内存在函数入口处分配(通常在堆栈上),并在函数返回时释放。在这种情况下,函数恰好是 main 的事实并不影响这一点。

这个:

cin >> arraySize;
int arr[arraySize];

是一个“可变长度数组”(VLA)。问题是,C++ 不支持 VLA。 C 可以,从 1999 年的 ISO C 标准 (C99) 开始,但它不是 C++ 采用的特性。

您的编译器支持 C++ 中的 VLA 作为扩展。使用它们会使您的代码不可移植。

(VLA 的一个问题是没有检测分配失败的机制;如果arraySize 太大,则程序的行为未定义)。

对于 gcc,使用 -pedantic 编译会产生警告:

warning: ISO C++ forbids variable length array ‘arr’

【讨论】:

    【解决方案4】:

    生成的代码在运行时在堆栈上分配 arraySize 字节。一旦函数返回,堆栈就会展开,包括“归还”为数组分配的字节。

    使用 new 和 delete 是为了在堆上分配空间。堆上分配的内存生命周期独立于任何函数或方法范围 - 如果您在函数中为其分配空间,并且函数返回,则内存仍然被分配并且有效。

    【讨论】:

      猜你喜欢
      • 2011-08-18
      • 1970-01-01
      • 2014-12-31
      • 2013-10-26
      • 1970-01-01
      • 2013-03-28
      • 2012-05-27
      • 2010-09-30
      • 1970-01-01
      相关资源
      最近更新 更多