【发布时间】:2021-06-27 17:02:23
【问题描述】:
以下是红龙书的节选。它处理过程激活记录中可变长度数据项的处理。
Pascal在语言中几乎是独一无二的,它要求过程本地的数组具有可以在编译时确定的长度。更常见的是,本地数组的大小可能取决于传递给过程的参数值。在这种情况下,在调用过程之前,无法确定过程本地的所有数据的大小。在图 7.15 中建议了处理可变长度数据的常用策略,其中过程
p具有三个局部数组。这些数组的存储不是p的激活记录的一部分;只有一个指向每个数组开头的指针出现在激活记录中。这些指针的相对地址在编译时是已知的,因此目标代码可以通过指针访问数组元素。图 7.15 中还显示了由
p调用的过程q。q的激活记录在p的数组之后开始,q的可变长度数组在此之后开始。
在上面摘录的第一部分中,文本讨论了Pascal 编程语言的特性,然后他们讨论了相同的可能实现。现在不熟悉Pascal,想了解C处理情况的方式。
我知道可以使用malloc 及其姐妹函数在C 中动态创建数组,这会导致在堆上分配内存并将指向第一个字节的指针返回给我们。根本不是问题。
如果我们在C 中创建数组,其中数组的大小是一个常量,如下所示:
int function() {
int a[100];
}
然后将数组放在激活记录的local data部分,如下所示:
在上面的例子中,数组a 的大小在编译时是已知的。没有问题。
现在的情况:“更多时候,本地数组的大小可能取决于传递给过程的参数值。在这种情况下,无法确定过程本地的所有数据的大小直到调用该过程。”
案例 1:
现在让我们考虑下面的代码:
int function(int n){
int a[n];
}
int main() {
function(10);
}
现在在上面的情况下,参数n 到function 的大小可以在编译时知道,因此数组a 的大小可以在编译时知道。这样的逻辑C是不是按照龙之书fig 7.15的方式分配上面的数组a?
案例 2:
int function(int n){
int a[n];
}
int main() {
int x;
scanf("%d",&x);
function(x);
}
现在在上面的情况下,参数n到function的大小只能在运行时知道(?)还是还是上面的情况,即在编译时知道? 现在在这段代码中,数组a 分配在哪里。堆还是栈?
我去了here、here 和here,但没有找到我正在寻找的解释...
这里说的是C99
【问题讨论】:
-
案例 1 中的函数具有外部链接(因此可以从其他翻译单元调用),因此编译器不知道
n的值是什么,因此它必须包含目标文件中的代码等同于案例 2 中的代码。但是,它没有义务使用案例 1 中的代码;它可以内联可见调用,当然在内联代码中n的值可以用它的已知值代替。但由于不再有函数调用,我认为内联案例与您的问题无关。 -
我的龙书(第一版,但不是第一次印刷,我无法确定何时印刷)没有你引用的关于帕斯卡的句子几乎是独一无二的。我的副本说的是“像 Pascal 这样的语言需要过程本地的数组......”。此外,该段落比您报价中的第二段早几页。当然,当本书写成时(我猜是在 1980 年代初期),C 还要求数组在编译时具有固定的大小。 VLA 直到 1999 年才标准化。
-
@rici 我也不知道确切的美国版印刷,因为我的是为我们国家的学生制作的低价版,而且也很旧(我选择了一个便宜的二手复制)。是的,第一段比第二段早几页。在这之间还有其他关于变量参数传递的东西。但是有一件事你能解释一下这部分吗:
it must therefore include code in the object file equivalent to the code in case 2.你指的是哪个code,你能说我......吗? -
我也不在美国(我是在欧洲买的)。我指的代码是编译器生成的代码。也就是说,编译器无法对将给外部链接函数提供哪些参数做出假设,因此它必须为该函数生成相同的代码,而不管它在正在编译的翻译单元中如何被调用。跨度>
-
@rici 好的!
标签: arrays compiler-construction c99 variable-length-array runtime-environment