【问题标题】:Understanding the handling Variable-Length Data with special focus on variable length arrays in C(99)了解处理可变长度数据,特别关注 C(99) 中的可变长度数组
【发布时间】:2021-06-27 17:02:23
【问题描述】:

以下是红龙书的节选。它处理过程激活记录中可变长度数据项的处理。

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

在图 7.15 中建议了处理可变长度数据的常用策略,其中过程p 具有三个局部数组。这些数组的存储不是p 的激活记录的一部分;只有一个指向每个数组开头的指针出现在激活记录中。这些指针的相对地址在编译时是已知的,因此目标代码可以通过指针访问数组元素。

图 7.15 中还显示了由p 调用的过程qq 的激活记录在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);
  }

现在在上面的情况下,参数nfunction 的大小可以在编译时知道,因此数组a 的大小可以在编译时知道。这样的逻辑C是不是按照龙之书fig 7.15的方式分配上面的数组a

案例 2:

  int function(int n){
           int a[n];
  }
  int main() {
        int x;
        scanf("%d",&x);
        function(x);
  }

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

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


这里说的是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


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-11
    • 2020-11-20
    • 2014-07-27
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    • 2014-06-17
    相关资源
    最近更新 更多