在this answer中,用户报告他的g ++版本编译了VLA,以及this question。在我的编译器上,这也有效(具有该扩展),但是如果我想将这样的数组声明为类成员,例如
中的position
和velocity
class PSO
{
private:
static double * data;
static int len;
static int dim;
double position [dim];
double velocity [dim];
double get_min();
double get_max();
void copy_data(double data *);
//...
它无法编译。这是什么原因?为什么编译器扩展允许main
函数中的VLA,而不是类成员的声明?
附录
我正在使用gcc 4.8.2
修改
我知道我应该使用std::vector
或指针,但我想知道为什么它会在main函数中编译而不是在类声明中编译。
编辑2
要清楚,这会编译(在我的情况下)
int main()
{
int size;
double data [size];
size = 5;
return 0;
}
这与完全完全相同,因为我在数组声明之前没有std::cin >> size
语句。有没有其他人在他们的编译器中有这个(怪癖)?
答案 0 :(得分:4)
VLA(可变长度数组)是1999 ISO C标准中添加到C语言的功能(并且根据2011标准可选)。 C ++标准尚未采用它们。
C ++中的VLA是gcc扩展。它们非常基于相同功能的C版本。 g ++不允许VLA作为类成员的很大一部分原因是C没有类。它确实具有与类非常相似的结构,但C不允许在结构中使用VLA。
将VLA实现为具有自动存储持续时间的对象(非static
局部变量)相对简单。通过在遇到声明时评估[
和]
之间的表达式来确定数组的长度,并确定必须分配多少空间(通常在堆栈上)。解除分配VLA通常是在离开声明对象的块时拆除堆栈帧的一部分。
作为班级或结构成员的VLA会更复杂。在您的示例中:
class PSO {
...
static int dim;
double position [dim];
double velocity [dim];
};
每次创建position
对象时,必须使用当前值velocity
来分配PSO
和dim
成员来确定长度。如果dim
没有为其分配值,那么它将是0
- 并且C和C ++都不允许零长度数组。如果已分配值,则必须在运行时计算velocity
的偏移量;这并非不可能,但通常班级和工会的成员都有不断的抵消。大概position
和velocity
的长度将由创建dim
对象时PSO
的值确定 - 但dim
可以在以后修改,使得很难确定任意PS0
对象的数组长度。
将sizeof
应用于VLA对象会在运行时计算大小。该大小通常存储在与VLA的类型关联的编译器创建的对象中。每个定义的VLA对象都有自己的类型。如果允许VLA作为类成员,则不同大小的数量可以是任意的,因为动态创建了多个PS0
对象。
这些困难都不是不可克服的。例如,Ada允许记录(类似于C和C ++结构)的成员是数组,其长度可以根据记录类型的每个实例而变化:
type Rec(Length: Natural) is
record
S: String(1 .. Length);
end record;
在C ++中支持这种语言扩展需要在编译器中进行大量工作。可能是gcc团队,因为他们必须为C实现VLA,因此决定以类似的方式为C ++实现它们将获得不错的回报而不需要太多努力。按照你的建议扩展它们需要相当多的工作(尤其是设计语义以便它们可以被一致地使用),因为它们可能没有考虑到足够的好处 - 特别是因为C ++在标准库容器类中具有更强大的功能
gcc对可变长度数组的支持描述为here,或者输入" info gcc"并搜索"可变长度阵列"。
答案 1 :(得分:3)
所以,让我们先说清楚。 C ++中的可变长度数组是一个编译器扩展。这不是通过C ++标准直接支持的。
因此,这里有两种不同的VLA正在讨论。堆栈分配和最后一个结构成员分配。让我们一次处理一个。
在这里,我们只是简单地谈论:
int main() {
int length = 12;
float array[length];
}
这非常简单。我们有一个指向我们当前正在使用的堆栈空间末尾的指针,当我们进入array
声明时,我们会扩展它。
这更复杂。支持的普通编译器扩展允许最后一个成员是可变长度数组。但我们只能以一种方式做到这一点:
struct MyObject {
int x;
float y;
double z[]; // Note that we didn't give this any length!
};
现在。通过此对象sizeof()
的大小是多少?它可能是8
。此大小为数组z
分配无空格。它假设它的长度为0.那么我们可以用它来做什么呢?
struct MyObject *object = malloc(sizeof(MyObject) + 8*sizeof(double));
这允许我们通过object->z
数组访问8个双打。
如果有足够的编译器扩展,我们可以做更多的事情。但问题是,一般来说,我们最多只想做一个指针加上一对(但是固定数量)的计算偏移到系统中的每个变量。这两个扩展并没有打破这种愿望。
允许这样的事情:
struct MyOtherObject {
int dim;
int x[dim];
int y[dim];
};
可以工作。但是,这是一个更复杂的扩展。而且它不包括在内。有一些问题,比如能够在分配发生后更改dim
。但最终,这些问题可以得到解决。
但编译器扩展不接受该代码,因为它超出了定义它的规范。
答案 2 :(得分:1)
使用alloca
/只修改堆栈指针来创建本地VLA非常容易。在一种类型中支持它有很多挑战(突破/指向成员等)。将其分配为对象的一部分将使其大小动态 - 使得赋值和指针算术等事情变得更加复杂。由于我们可以使用指向对象外部数据的指针,因此对于类型来说,它似乎不值得支持它。
答案 3 :(得分:0)
这绝对是一种猜测,我最近主要使用C编程。也许是因为类和结构需要具有标准大小(以字节为单位),因此它们可以以固定间隔线性存储在内存中,同时也可以通过使用sizeof的代码进行遍历。如果他们允许VLA,那么一个对象可能有一个16字节的数组,而另一个对象可能有一个16000字节的数组。然后编译器如何使代码遍历不同长度的对象?我的猜测:它只是称这是一个错误,并没有打扰。
我确定你可以通过使用malloc和指针解决这个问题。
答案 4 :(得分:0)
如果在运行时之前不知道数组的长度,则应使用指针。在您的示例中,将执行以下操作:
class PSO
{
private:
static double * data;
static int len;
static int dim;
double* position;
double* velocity;
double get_min();
double get_max();
void copy_data(double data *);
//...
}
然后,您将在代码中的某个位置实例化指针(例如PSO
的构造函数),如下所示:
position = new double[dim];
velocity = new double[dim];
请记住,您不能在类定义中定义变量,只能声明它们。
不要忘记 - 因为您使用C ++编写,您还可以访问其他结构,例如vector
,它可以在动态大小的数组中保存变量。