为什么我可以声明一个尺寸大小可变但不是新尺寸的二维数组?

时间:2013-06-01 14:28:19

标签: c++ arrays llvm compiler-optimization language-features

正如问题所述,这是可行的:

#include <iostream>

int main(int argc, char *argv[])
{
    unsigned short int i;
    std::cin >> i;
    unsigned long long int k[i][i];
}

这里我声明了一个大小为i的数组,两个维都是变量。

但不是这样:

#include <iostream>

int main(int argc, char *argv[])
{
    unsigned short int i;
    std::cin >> i;
    unsigned long long int** k = new int[i][i];
    delete[] k;
}

我收到一条编译器消息告诉我

  

错误:只有已分配数组的第一维可能具有动态   大小

我被迫这样做:

#include <iostream>

int main(int argc, char *argv[])
{
    unsigned short int i;
    std::cin >> i;
    unsigned long long int** k = new unsigned long long int*[i];
    for ( unsigned short int idx = 0 ; idx < i ; ++ i )
        k[idx] = new unsigned long long int[i];
    for ( unsigned short int idx = 0 ; idx < i ; ++ i )
        delete[] k[idx];
    delete[] k;
}

根据我的理解,new和delete用于在堆上分配内容,而不是在堆栈上,当它超出范围时不会被删除,并且对于跨函数和对象传递数据等非常有用。

我不明白当我在第一个例子中声明k时会发生什么,我被告知声明的数组应该(并且可能)只有常量维度,并且当需要一个数组时未知的大小,应始终考虑new&amp; deletevector s。

这两种我没有得到的解决方案是否有任何利弊,或者它是什么呢?

顺便说一下,我正在使用Apple的LLVM编译器。

3 个答案:

答案 0 :(得分:1)

这两种形式都不符合C ++标准,因为标准不支持可变长度数组(VLAs)(有趣的是,C99可以 - 但C不是C ++)。但是,有几个编译器有一个扩展来支持它,包括你的编译器:

From Clang's Manual

  

Clang在非常有限的情况下支持这种可变长度数组,以便与GNU C和C99程序兼容:

     
      
  • 可变长度数组的元素类型必须是POD(“普通旧数据”)类型,这意味着它不能具有任何用户声明的构造函数或析构函数,任何基类或任何非POD类型的成员。所有C类型都是POD类型。
  •   
  • 可变长度数组不能用作非类型模板参数的类型。
  •   

但鉴于扩展已到位,为什么你的第二个片段不起作用?那是因为 VLA仅适用于自动变量 - 即参数或局部变量。 k是自动的,但它只是一个指针 - 数组本身由new int[i][i]定义,它在堆上分配,并且显然不是自动变量。

您可以在the relevant GCC manual section上了解详情。

答案 1 :(得分:0)

我确信您可以轻松找到2D阵列功能的实现,但您也可以创建自己的类。最简单的方法是使用std::vector来保存数据并使用索引映射函数来获取两个坐标并将单个索引返回到向量中。

客户端代码看起来会有所不同,而不是arr[x][y]你有arr.at(x,y)但是它会做同样的事情。您不必像std::vector那样完成内存管理,只需在构造函数或维度设置函数中使用v.resize(N*N)

答案 2 :(得分:0)

基本上编译器通常对二维数组(固定或变量)执行的操作是:

int arr[x][y] ---> int arr[x*y];

arr[2][4]= something ---> arr[2+4*x]= something;

基本上它们只是一种更好的一维数组符号(在堆栈上)。大多数编译器都需要固定的大小,因此编译器可以更容易地告诉维度是什么(以及因此需要乘以什么)。看来你只有一个编译器,即使你使用变量也可以跟踪维度(和乘数)。

当然你也可以自己模仿new [],但编译器本身并不支持它。

可能出于同样的原因,即因为跟踪尺寸会更加困难,特别是在移动指针时。

E.g。使用新指针,您稍后可以写:

newarr= someotherarray;

someotherarray可能是具有不同维度的东西。如果编译器做了2-dim - >一个昏暗的翻译,他必须跟踪所有可能的大小过渡。

上面的堆栈分配arr,这不是必需的,因为至少在编译器完成后,它就会保持这个大小。