【问题标题】:Static hint in variable length arrays可变长度数组中的静态提示
【发布时间】:2012-08-25 17:15:21
【问题描述】:

我对 C99 中的区别有点困惑:

int myfunc (int array[n], int n) { ... }

不会编译。据我所知,您必须始终将对数组大小的引用放在首位,因此必须这样写:

int myfunc (int n, int array[n]) { ... }

但如果你提供 static 关键字,这绝对没问题:

int myfunc (int array[static 1], int n) { ... }

这个顺序对我来说更可取,因为我习惯于在函数调用中将数组放在第一位,但为什么会这样呢?

编辑:意识到第三个示例实际上不是 VLA 有助于...

作为参考,这是我正在查看的导致问题的一段代码:

int sum_array(int n, int m, int a[n][m])
{
  int i, j, sum = 0;
  for (i = 0; i < n; i++)
    for (j = 0; j < m; j++)
      sum += a[i][j];
  return sum;
}

【问题讨论】:

  • 你说的“这绝对没问题”是什么意思?为了用static 创建一个可比较的例子,你必须这样写int myfunc (int array[static n], int n)。由于同样的原因,它不会“正常工作”。您上一个示例“工作正常”,因为您将n 替换为1static 与此无关。
  • 它的工作方式与之前的定义完全相同(只要您切换函数参数,显然)——两者都将采用任意长度的数组并返回相同的结果。
  • int myfunc (int array[], int n) 的工作方式与int myfunc (int array[static 1], int n) 相同,只是在第一个原型中array 参数不能是NULL
  • @teppic:您的所有声明都将采用任意长度的数组(最后一个声明“不小于 1”)。但是,你又问为什么第三个声明是可能的。我不明白是什么引发了这个问题。为什么不呢?第三个声明中究竟有什么令您惊讶的地方?
  • @teppic 最后一种形式与 VLA 无关,它是作为实现的优化提示引入的,您可以将其与非可变长度数组一起使用。

标签: c arrays c99


【解决方案1】:

编辑:当然是 Derp,范围。

我的问题是;为什么你需要这样做?无论如何,您确实得到了一个指针(您不能将数组传递给 C 中的函数,它们会降级为指针,而不管函数的签名如何)。它有助于让调用者知道输入的预期大小,但除此之外它就没用了。由于他们已经通过了大小,只需使用...

int myfunc(int arr[], size_t size) {
    // ...
}

或者

int myfunc(int *arr, size_t size) {
    // ...
}

【讨论】:

  • 在这种情况下,参数的顺序很重要——第二种形式总是可以编译,第一种永远不会。我在这里写的实际函数只是为了演示我的问题。
  • @teppic:是的,没错。这并没有改变这样一个事实,即这是无用且浪费时间的。你仍然会得到一个指向函数中第一个元素的指针。
  • 是的,这里的函数本身并不重要,只是语法让我感到困惑。我想这里的表单相对于传统的 arr[] 的主要优点是创建的数组的大小肯定是 n。
  • @teppic:没有“创建”数组;您只需收到指向第一个元素的指针,而不管大小。您不能将数组传递给函数;它们退化为指针。
  • @teppic:在你的函数中试试这个; size_t s = sizeof(arr)s 将始终等于 sizeof(int*)
【解决方案2】:

这是因为数组必须用常量值声明,所以您不能使用可变大小创建数组,因此不能传递具有可变大小的数组。此外,如果它只是一个单维数组,则根本不需要传递值,那就是传递第二个参数来告诉您数组的长度。

要让它正常工作,只需像这样编写函数头:

int myfunc (int myArray[], int n) {...}

顺序无关紧要,但你不能让你传递的数组的大小是可变的,它必须是一个常量值。

【讨论】:

  • 您的建议是让程序编译的简单方法,但 C99 确实 引入了称为“可变长度数组”的东西,当上下文明确时,通常缩写为 VLA,正如您在围绕这个问题的讨论中看到的那样。 (我没有对您投反对票,但无论是谁都可能对您回答中的这种技术性感到恼火)
【解决方案3】:

原因

int myfunc (int n, int array[n]) { ... }

有效且

int myfunc (int array[n], int n) { ... }

不是由于 C 的词法范围规则。标识符在被引入范围之前不能使用。这条规则有一些例外,但这不是其中之一。

编辑:这是 C 标准的相关段落:

(C99, 6.2.1p7) “任何其他标识符的范围都在其声明符完成后开始。”

此规则也适用于函数原型范围内的参数声明。

【讨论】:

    【解决方案4】:

    如果您正在使用 GCC 并愿意使用他们的一些扩展,您可以在这里完成您想要的:

    int myFunc (int len; /* notice the semicolon!! */ int data[len], int len)
    {
    
    }
    

    此扩展(可变长度数组)的文档是here

    请注意,由于某种原因,此扩展在 clang 中不可用,但我不太清楚为什么。

    【讨论】:

      【解决方案5】:

      错误原因已经给你解释过了:你必须声明n之前你可以在其他声明中使用它。

      然而,值得注意的是,这些声明实际上都没有像您所相信的那样声明可变长度数组。

      确实,[n] 的语法首先在 C99 中被允许,并且它正式是一个 VLA 声明,但是在给定的上下文中,所有这些声明都将 array 声明为 int * 类型的参数,只是就像它一直在 C89/90 中一样。 [n] 部分不是任何形式的提示。您可以在此声明中使用 [n] 的事实确实是 VLA 支持的副作用,但这是与 VLA 的任何关系结束的地方。 [n] 被忽略了。

      “提示”声明需要在 [] 中使用关键字 static。因此,您使用[static 1] 的声明等同于经典的int array[1] 声明(意味着1 被忽略并且参数的类型为int *),只是它向编译器提示至少必须存在1 元素在array指向的内存位置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-27
        • 2012-01-29
        • 2020-11-20
        相关资源
        最近更新 更多