当大小是变量而不是常量时,如何创建数组?

时间:2013-01-22 13:05:03

标签: c++ c constants array-initialization

我有一个接收变量int的方法。该变量构成一个数组大小(请不要给我一个向量)。因此,我需要在我的方法中初始化一个const int来初始化一个特定大小的数组。问题:我该怎么做?

void foo(int variable_int){
    int a[variable_int] = {0}; //error
}

6 个答案:

答案 0 :(得分:7)

int *a = new int[variable_int];

完成后,请记得删除已分配的空格!

答案 1 :(得分:4)

C ++不支持可变长度数组。相反,您需要动态分配数组:

std::vector<int> a(variable_int);

或者因为你说你出于某种原因不想使用矢量:

class not_a_vector
{
public:
    explicit not_a_vector(size_t size) : a(new int[size]()) {}
    ~not_a_vector() {delete [] a;}
    int & operator[](size_t i) {return a[i];}
    int   operator[](size_t i) const {return a[i];}

    not_a_vector(not_a_vector const &) = delete;
    void operator=(not_a_vector const &) = delete;

private:
    int * a;
};

not_a_vector a(variable_int);

更新:问题刚刚用“C”标签和“C ++”更新。 C(自1999年以来)确实支持可变长度数组,因此您的代码应该适合该语言。

答案 2 :(得分:4)

你问过一个非矢量解决方案,但让我们重复一遍,因为你可能因为错误的原因拒绝了它。您不应该担心性能,因为在任何合格的编译器手中,它将具有与任何其他标准符合性解决方案几乎相同的开销。另一方面,我将在下面介绍一些可读性和安全性问题。让我们看一下从最推荐到最不推荐的方式。

的std ::矢量

社区最喜欢的容器,有充分的理由。它不仅可以使用运行时大小声明,而且可以随时更改大小。当不能预先确定大小时,例如当重复轮询用户输入时,这便于使用。例子:

// Known size
size_t n;
std::cin >> n;
std::vector<int> vec(n);

// Unknown size
std::vector<int> vec;
int input;
while (std::cin >> input) { // Note: not always the best way to read input
    vec.push_back(in);
}

使用std::vector并没有太大的缺点。已知大小的情况只需要一个动态分配。在一般情况下,未知大小需要更多,但无论如何你都无法做得更好。因此,性能或多或少是最佳的。

从语义上讲,对于在整个执行过程中保持不变的大小,它可能并不理想。读者可能不明白这个容器不是要改变的。编译器不知道它是否允许你在push_back中做一些错误的事情,而vector逻辑上是恒定大小。

std :: unique_ptr(或std :: shared_ptr)

如果强制执行静态大小对您来说最安全的解决方案。

size_t n;
std::cin >> n;
auto arr = std::make_unique<int[]>(n);

arr的大小不能改变,尽管可以释放当前数组并指向另一个不同大小的数组。因此,如果逻辑上容器的大小是恒定的,这将以更清晰的方式传达意图。不幸的是,即使在恒定大小的情况下,它也比std::vector弱得多。它不是可识别大小的,因此您必须明确存储大小。出于同样的原因,它不提供迭代器,也不能在for循环中使用。如果您想牺牲这些功能来强制执行静态大小,则由您(以及相关项目)决定。

最初我曾推荐过boost::scoped_array,但经过进一步的考虑后,我不相信它对这个解决方案提供了太多帮助,所以我会坚持使用标准库。

new [] - 删除[]

从技术上讲,这是一个解决方案,但除非您被迫使用旧的C ++标准,或者您正在编写一个内部管理内存的低级库,否则它们严格地比std::unique_ptrstd::shared_ptr解决方案更糟糕。它们没有提供更多功能,但安全性显着降低,因为在完成内存后必须明确释放内存。否则,您将泄漏它,这可能会导致严重的问题。更糟糕的是,正确使用delete[]对于具有复杂的执行流程和异常处理的程序来说可能是非常重要的。如果您可以使用上述解决方案,请不要使用此功能!

size_t n;
std::cin >> n;
int* arr = new int[n];
...
// Control flow must reach exactly one corresponding delete[] !!!
delete[] arr;

奖励:编译器扩展

有些编译器实际上可以使用以下代码

size_t n;
std::cin >> n;
int arr[n];

依靠这有严重的缺点。您的代码无法在所有符合C ++标准的编译器上编译。它可能甚至不能在给定编译器的所有版本上编译。此外,我怀疑生成的可执行文件检查n的值并在需要时在堆上分配意味着您可以炸毁堆栈。当您知道n的上限很小并且性能对您如此重要以至于您愿意依赖特定于编译器的行为来获取它时,此解决方案才有意义。这些都是非常特殊的情况。

答案 3 :(得分:3)

您可以通过编写const int bar = variable_int;轻松地从非const变量创建一个const变量 - 但这对您没有帮助。在C ++中,具有自动存储的数组的大小必须是编译时常量。你不能把变量变成编译时常量,所以你想要的就是不可能。

根据您的需要,您可以使a指针并使用new(稍后delete)来分配内存,或者如果参数foo将会{总是在编译时知道,你可以将foo变成这样的模板函数:

template<int n> void foo() {
    int a[n] = {0};
}

答案 4 :(得分:2)

要做你想做的事,你需要使用动态分配。在这种情况下,我会认真建议使用向量 - 这是在C ++中做的“正确”的事情。

但如果你仍然不想使用矢量[为什么你不会超越我],正确的代码是:

 void foo(int variable_int){
    int *a   = new int[variable_int]();   // Parenthesis to initialize to zero.
    ... do stuff with a ... 
    delete [] a;
 }

正如其他人所建议的那样,你也可以使用calloc,它具有与初始化为零相同的效果,但不是真正的“c ++”解决方案。

答案 5 :(得分:-1)

如果你正在使用数组,那么封装它们是个好主意:

template<typename Type>
class Vector {
    //...
};

标准库附带一个实现:std::vector