我有一个接收变量int的方法。该变量构成了一个数组的大小(请不要给我提供一个vector)。因此,我需要在我的方法中初始化一个const int,以初始化特定大小的数组。问题是:我该怎么做?
void foo(int variable_int){
int a[variable_int] = {0}; //error
}
我有一个接收变量int的方法。该变量构成了一个数组的大小(请不要给我提供一个vector)。因此,我需要在我的方法中初始化一个const int,以初始化特定大小的数组。问题是:我该怎么做?
void foo(int variable_int){
int a[variable_int] = {0}; //error
}
你要求一个非向量解决方案,但我们还是来讨论一下,因为你可能因为错误的理由而拒绝它。你不用担心性能,因为在任何一款称职的编译器手中,它的开销几乎和任何其他标准符合的解决方案一样。另一方面,下面我会讲到一些可读性和安全性方面的问题。让我们从最推荐的方法到最不推荐的方法来看看如何实现非向量解决方案。
社区最喜爱的容器之一,原因很明显。它不仅可以声明具有运行时大小,而且大小可以随时更改。这在大小无法预定的情况下非常有用,例如当不断轮询用户输入时。例子:
// 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
的劣势并不大。在已知大小的情况下,需要恰好一个动态分配。未知大小在一般情况下需要更多,但你无论如何都无法做得更好。因此性能基本上是最优的。
从语义上讲,对于在执行过程中保持不变的大小可能不是理想的选择。读者可能不会意识到这个容器不打算改变。编译器也不知道,所以它会允许你做一些“错误”的事情,比如向vector
中push_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
,但经过进一步思考,我不认为它比这个解决方案有什么优势,所以我会坚持使用标准库。
从技术上讲,这是一种解决方案,但除非你被迫使用旧的C++标准或者你正在编写一个内部管理内存的低级库,否则它们比std::unique_ptr
或std::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
的上限很小且性能对您非常重要,以至于您愿意依赖特定于编译器的行为来获取它时,此解决方案才有意义。这些是真正异常的情况。int *a = new int[variable_int];
std::vector<int> a(variable_int)
。现在你不需要记得删除它,或者调试内存泄漏,否则如果抛出异常,内存泄漏将不可避免地出现。 - Mike Seymouralloca
,int *a = alloca(variable_int);
,与此答案类似,它不会将内存初始化为特定值,但具有非常快的分配方案的优点:内存在堆栈上分配,而不是在堆上,因此您也不会遇到内存异常(这里可能会出现堆栈空间不足的情况,但那时您就会遇到更大的问题)。对于小型分配(最多几千字节),请使用此技巧。 - radical7delete[]
而不是 delete
!虽然在这种特定情况下并不重要,因为没有析构函数。 - Jack AidleyC++不支持可变长度数组。相反,您需要动态分配数组:
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语言就支持可变长度数组,因此您的代码在该语言中应该没问题。
const int bar = variable_int;
很容易地从非常量变量创建一个常量变量- 但这并没有帮助你。在C ++中,具有自动存储的数组的大小必须是一个编译时常量。你无法将变量转换为编译时常量,因此你想要的根本不可能实现。a
成为指针,并使用new
分配内存(然后稍后用delete
释放它),或者如果foo
的参数总是在编译时已知,你可以将foo
转换为这样的模板函数:template<int n> void foo() {
int a[n] = {0};
}
如果你想要做到你想做的事情,你需要使用动态分配。在这种情况下,我强烈建议使用向量而不是数组——在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 ++”解决方案。