了解处理变长数据,特别关注 C(99) 中的变长数组

问题描述 投票:0回答:1

以下是编译器:原理、技术和工具的摘录。它涉及过程激活记录中可变长度数据项的处理。

Pascal 在要求过程本地数组具有可在编译时确定的长度的语言中几乎是独一无二的。更常见的是,本地数组的大小可能取决于传递给过程的参数值。在这种情况下,在调用该过程之前,无法确定该过程本地的所有数据的大小。**

图 7.15 中提出了处理可变长度数据的通用策略,其中过程

p
有三个局部数组。这些数组的存储不是
p
激活记录的一部分;激活记录中只出现指向每个数组开头的指针。这些指针的相对地址在编译时是已知的,因此目标代码可以通过指针访问数组元素。

图 7.15 中还显示了由

q
调用的过程
p
q
的激活记录在
p
数组之后开始,
q
的可变长度数组在此之后开始。

在上面摘录的第一部分中,文本讨论了 Pascal 编程语言的功能,然后讨论了该语言的可能实现。现在我不熟悉 Pascal,想了解一下 C 中处理情况的方式。

我知道可以使用 malloc() 及其姊妹函数在 C 中动态创建数组,这会导致在堆上分配内存,并将指向第一个字节的指针返回给我们。这根本不是问题。

如果我们在 C 中创建数组,其中数组的大小是一个常量,如下所示:

int function() {
   int a[100];
}

然后将数组放置在激活记录的本地数据部分,如下所示:

在上面的示例中,数组

a
的大小在编译时就已知。没有任何问题。

现在的情况: “更常见的是,局部数组的大小可能取决于传递给过程的参数值。在这种情况下,直到过程被执行后,才能确定过程本地的所有数据的大小。打电话了。”

案例1:

现在让我们考虑下面的代码:

int function(int n) {
    int a[n];
}

int main() {
    function(10);
}

现在在上面的情况下,函数参数

n
的大小可以在编译时知道,因此数组
a
的大小虽然可以在编译时知道。按照这个逻辑,C 是不是按照龙书图 7.15 的方式来分配上面的数组
a

案例2:

int function(int n) {
    int a[n];
}

int main() {
    int x;
    scanf("%d", &x);
    function(x);
}

现在在上面的情况下,函数参数

n
的大小只能在运行时知道(?)还是仍然是上面的情况,即在编译时知道?现在在这段代码中,数组
a
在哪里分配。堆还是栈?

我去了这里这里这里,但我没有找到我正在寻找的解释...


这里我说的是C99

arrays compiler-construction c99 variable-length-array runtime-environment
1个回答
2
投票

一般来说,C 实现在堆栈上分配 VLA,而不是在单独的堆数据结构中,但这实际上并不是标准所要求的。由于 VLA 的大小仅在运行时已知,这意味着您将获得此类函数的可变大小的激活记录。激活记录如何分配和布局的细节往往取决于目标机器及其 ABI,因此尝试将其分解(如上图所示)对于不同的编译器和体系结构来说将是非常不同的。

© www.soinside.com 2019 - 2024. All rights reserved.