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

4

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

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

8
翻译:向量有什么问题吗?如果不行的话,我可以给你一个智能指针。 - chris
11
为什么不使用向量?如果你只想使用 C 语言的习惯用法,为什么还要使用 C++? - Paul R
4
将向量看作只是一个数组,您可以用变量大小来初始化。您确定您不是想给您的问题打上“C”标签吗? - Roddy
4
对于大多数情况而言,C++中的“向量(vector)”至少与数组一样好用!(如果你之前没有使用过C++向量:名字可能会让人误解,它们的行为更像一个数组而不是数学意义上的向量。)甚至可以使用“[]”运算符访问向量元素(如果不需要或者不想进行边界检查的话)。 - us2012
4
答案是使用向量。C++不支持可变长数组,如果没有RAII的内存管理将会导致内存泄漏,如果有异常抛出。 - Mike Seymour
显示剩余4条评论
6个回答

15

你要求一个非向量解决方案,但我们还是来讨论一下,因为你可能因为错误的理由而拒绝它。你不用担心性能,因为在任何一款称职的编译器手中,它的开销几乎和任何其他标准符合的解决方案一样。另一方面,下面我会讲到一些可读性和安全性方面的问题。让我们从最推荐的方法到最不推荐的方法来看看如何实现非向量解决方案。

std::vector

社区最喜爱的容器之一,原因很明显。它不仅可以声明具有运行时大小,而且大小可以随时更改。这在大小无法预定的情况下非常有用,例如当不断轮询用户输入时。例子:

// 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的劣势并不大。在已知大小的情况下,需要恰好一个动态分配。未知大小在一般情况下需要更多,但你无论如何都无法做得更好。因此性能基本上是最优的。

从语义上讲,对于在执行过程中保持不变的大小可能不是理想的选择。读者可能不会意识到这个容器不打算改变。编译器也不知道,所以它会允许你做一些“错误”的事情,比如向vectorpush_back,尽管在概念上它应该有一个常数大小。

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[] - delete[]

从技术上讲,这是一种解决方案,但除非你被迫使用旧的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的上限很小且性能对您非常重要,以至于您愿意依赖特定于编译器的行为来获取它时,此解决方案才有意义。这些是真正异常的情况。

9
int *a = new int[variable_int];

记得在完成后删除分配的空间!

9
更好的方式是std::vector<int> a(variable_int)。现在你不需要记得删除它,或者调试内存泄漏,否则如果抛出异常,内存泄漏将不可避免地出现。 - Mike Seymour
2
@MikeSeymour同意,但是不使用向量是问题的要求。 - zennehoy
6
抱歉,我没注意到那个奇怪的要求。祝你好运,解决内存泄漏问题。 - Mike Seymour
1
另一种简单的方法是使用allocaint *a = alloca(variable_int);,与此答案类似,它不会将内存初始化为特定值,但具有非常快的分配方案的优点:内存在堆栈上分配,而不是在堆上,因此您也不会遇到内存异常(这里可能会出现堆栈空间不足的情况,但那时您就会遇到更大的问题)。对于小型分配(最多几千字节),请使用此技巧。 - radical7
2
@MikeSeymour - 我同意你的观点:使用向量。遵循奇怪的要求只会浪费时间。 - Pete Becker
1
请记得使用 delete[] 而不是 delete!虽然在这种特定情况下并不重要,因为没有析构函数。 - Jack Aidley

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++"标签。自从1999年起,C语言就支持可变长度数组,因此您的代码在该语言中应该没问题。


3
你可以通过写const int bar = variable_int;很容易地从非常量变量创建一个常量变量- 但这并没有帮助你。在C ++中,具有自动存储的数组的大小必须是一个编译时常量。你无法将变量转换为编译时常量,因此你想要的根本不可能实现。
根据你的需求,你可以使a成为指针,并使用new分配内存(然后稍后用delete释放它),或者如果foo的参数总是在编译时已知,你可以将foo转换为这样的模板函数:
template<int n> void foo() {
    int a[n] = {0};
}

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 ++”解决方案。


2
不要忘记为异常安全性添加try catch ;-)值得一提的是,std::vector为你做了多少工作! - Roddy
谢谢你加了括号,不然我会出现段错误 :-) - Denys S.

-1
如果您正在使用数组,封装它们是一个好主意:
template<typename Type>
class Vector {
    //...
};

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接