什么时候变长数组是合法的?

When are variable-length arrays legal?

本文关键字:数组 什么时候      更新时间:2023-10-16

我不是c++专家,但据我所知,这段代码应该失败,因为size不是恒定的:

#include<iostream>
using namespace std;
int main(int argc, char** argv)
{
  int size = *argv[1] - 48;
  char array [size];
  cout<<sizeof(array)<<endl;
  return 0;
}

当我用gcc(最好说是g++)编译时,为什么这个工作?

./test 7
7
/test 2 
2

要从堆栈或堆中为变量分配内存,需要知道变量的大小。c++编译器可以自己决定它们如何分配内存,但是c++已经公开了它们期望c++编译器如何处理这种情况,因此c++ std要求编译器供应商发布它们的内存处理。这是通过sizeof操作符实现的。此操作符完全在编译时计算。对数组大小的编译时限制来自于这个要求。

int arr[10];
std::cout << sizeof(arr) << std::endl

因为每个变量和类型都支持sizeof,所以它们的大小需要在c++编译时计算。因此,可变长度数组在c++中是不可能的。

这个要求还有另一个非常重要的限制。原则上,c++编译器供应商可以计算c++程序栈所需的最大内存量,只要不存在一个问题:对于递归函数,您无法计算程序使用的堆栈大小,但对于其他所有功能,堆栈大小可以通过以下操作计算:

  1. 对堆栈帧
  2. 中的每个变量使用sizeof(a)
  3. 将变量的大小相加,以获得该堆栈帧所需的内存量
  4. 列出所有可能的堆栈帧并计算它们的大小
  5. 选择具有最大大小的调用堆栈
  6. 选择该大小作为程序栈的大小。

不幸的是,递归函数破坏了整个方案。并且需要全局程序流分析来识别哪些函数可能有无限的调用栈。但是编译时sizeof操作符的限制很重要,否则我们的c++程序会随机耗尽堆栈空间,导致崩溃和不稳定。这是不可接受的。因此,每个变量和类型都支持编译时sizeof操作符。

VLA支持要求编译器可以生成这样的代码:通常生成的作为结果机器码常量的偏移量实际上可以在运行时修改。符合标准的c++编译器通常不具备这种能力。C决定添加这种支持,因此C编译器可以做到这一点。但是在这个过程中,他们需要打破sizeof运算符。不再可以在编译时计算大小。C标准中指定的VLA支持存在很大的问题:

  1. 不能将VLA放在结构体或类中
  2. VLA基本上局限于局部函数范围

这些问题在c++中已经通过std::vector解决了,而std::vector不存在这些问题

以下是C99增加可变长度数组的新特性列表。

也见N1548的数组声明符 (ISO/IEC 9899:201x Committee Draft - December 2, 2010 N1548),其中详细说明。

这是一个非标准的GCC扩展-其他编译器如Visual c++不支持。

这是C99的一个特性,允许你在堆栈上声明这样的数组

c99支持可变长度数组(VLA),但c90和c++都不支持可变长度数组,但gcc在C和c++中都支持此扩展,如果使用这些参数进行编译,您可以更清楚地看到这一点:

gcc -std=c89 -pedantic

这会给你以下警告:

warning: ISO C90 forbids variable length array ‘array’ [-Wvla]

或与g++:

g++ -pedantic

会给你这样的警告:

warning: ISO C++ forbids variable length array ‘array’ [-Wvla]

gcc手册中的standards部分将详细介绍。值得注意的是,自2011年C标准可变长度数组(VLA)现在是可选的。

即使没有VLA扩展,当编译器在编译时未能推断出维度表达式是未知的时,代码也可以编译。还是UB

因为您没有将g++作为c++编译器调用。如果我尝试它,我得到一个警告,明确指出"ISO c++禁止可变长度数组"。但是我的makefile包含选项-std=c++98,至少当我想编译可移植的c++时。