如何声明一个数组而不进行常量初始化?

10

我尝试使用这个版本:

int n;
cin >> n;
int a[n]; // compiler error

但它不起作用。我做错了什么?


7
可变长度数组(VLAs)在标准C++中不受支持。考虑使用std::vector代替。 - Algirdas Preidžius
2
请注意,尽管“使用new”是以下所有答案中的第一个建议,但应优先选择std::vector,因为它更安全、更方便。 - HolyBlackCat
4个回答

12

有三种标准符合要求的方法可以在编译时声明一个大小未知的数组。按推荐程度从高到低介绍。

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没有太多的缺点。在已知大小的情况下,需要一次动态分配。在未知大小的情况下,在一般情况下需要更多动态分配,但无论如何您都无法做得更好。因此性能基本上是最佳的。

从语义上讲,对于整个执行过程中保持不变的大小可能并不理想。读者可能不会意识到该容器不打算更改。编译器也不知道这一点,因此它允许您在逻辑上大小恒定的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循环中使用。如果要强制使用静态大小,则由您(和相关项目)决定是否愿意牺牲这些功能。

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的上限很小并且性能对您非常重要以至于您愿意依赖特定于编译器的行为来实现它时,此解决方案才有意义。这些是真正的特殊情况。

1
请将这个答案发布到重复的旧问题中,因为它比现有的答案更详细,并且解释更好。 - Ben Voigt
@BenVoigt 或许将那个较旧的问题作为这个问题的重复? - Matt
@BenVoigt 我实际上已经在那里留下了一个答案,但它不如这个完整,因为由于某种原因,OP要求提供非向量解决方案。也许它们不应该被视为重复的。我认为这是一个更有建设性的问题。 - patatahooligan
我认为先解释为什么向量是首选,然后提供另一个智能指针,再展示裸指针方法,对于那个问题来说是完美的。 - Ben Voigt
@BenVoigt 我为了完整性而更新了它,但我仍然相信这应该是一个开放的问题。它更加通用(包含其他问题),得到更多的赞同,并且答案更加新颖和完整。 - patatahooligan

5
您可以在堆上分配数组:
int n;
cin >> n;
int *a = new int[n];

// use 'a'

delete[] a;

你也可以使用一个 std::vector

int n;
cin >> n;
vector<int> a(n);

你可以使用一个数组的数组(“矩阵”):
int n, m;
cin >> n >> m;
int **a = new int*[n];
for(int i = 0; i < n; i++)
    a[i] = new int[m];

// use 'a'

for (int i = 0; i < n; i++)
    delete[] a[i];

delete[] a;

你还可以拥有一个向量的向量


在C++中,带有非常量nvector<int> a[n];是无法工作的。 - HolyBlackCat
@HolyBlackCat 是的,你说得完全正确,我不知道那个。你知道如何使用向量声明动态矩阵吗? - Carl Johnson
例如,通过使用向量的向量。 - HolyBlackCat
如果你想声明一个动态矩阵,那么请使用 new int[n*m] 并进行索引访问的包装。我再次强调这一点! - Lightness Races in Orbit
@LightnessRacesinOrbit:当然,只需使用智能指针,而不是new[],可以使用make_unique<int[]>(n*m)std::vector<int>(n*m)。索引计算完全相同。 - Ben Voigt
@BenVoigt:没错。 - Lightness Races in Orbit

2

谷歌:C++ 动态数组

int n;
cin >> n;
int *a = new int[n];

// use 'a'

delete[] a;

5
如果你使用动态数组,结束时也必须进行清理:delete[] a;否则内存仍然被保留。 - Thomas
因为你正在堆上创建数组而不是栈上... - Thomas

2

C++没有变长数组。作为替代方案,可以使用动态数组,如@user70960所提到的:

int n(0);
cin >> n;
int *a(new int[n]);

// use 'a'

delete[] a;

你也可以使用向量来执行相同的操作,如下所示:

int n(0);
cin >> n;
vector<int> a(n);

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