为什么在函数的VLA数组参数中使用星号“[*]”而不是整数?

36

当在函数中使用可变长度数组作为参数时

int sum(int n, int a[n]);

很容易理解第一个参数(n)指定了第二个参数(a)的长度。但是当遇到另一种用作可变长度数组参数的原型时,就会遇到问题。

int sum(int n, int a[*]);

为什么在[]中使用*而不是n真的很难理解?


2
y@aaronman; 是的,这是C99的特性。 - haccks
2
那么... int a[] 是指针数组还是可变长度数组?仅凭代码很难理解... 我的意思是..."int a[]"与"int *a[]"或者只是"int a[]"是一样的吗? - Alejandro Iván
4
有关/重复问题,请参阅Jens Gustedt的答案。他解释了变长数组在C语言中的用法和限制。 - ajp15243
@ajp15243;这是不同的。 - haccks
2
如果你读完他的回答,你会发现他本质上讲解了AndreyT之下的问题,那就是如果在函数原型中不命名前面的长度参数,则[*]是非常有用的。 - ajp15243
显示剩余8条评论
2个回答

55
[*]语法用于声明函数原型。关键细节在于,在函数原型中,您不需要为参数命名,只需指定每个参数的类型。
例如,如果您将第一个参数保持未命名,那么显然您将无法在第二个(数组)参数声明中使用n。但是,在许多情况下,您需要告诉编译器某些参数是VLA。这是[*]语法发挥作用的时候。
在您的情况下,如果省略了参数名称,则原型可能如下所示:
int sum(int, int [*]);

然而,需要注意的是,在你自己的特定示例中,这种语法是合法的,但并不是完全必要的。就像对于非VLA数组一样,int [n]参数仍然等同于int *参数(即使对于非常量的n)。这意味着您可以将函数原型简单地定义为:

void functionName(int *array, int n);
int sum(int, int []);

或作为

int sum(int, int *);

原型仍然能够完成其目的,即它将正确匹配函数定义。换句话说,一个声明为1D数组的参数的VLA属性完全没有影响,因此在这种情况下不真正需要使用[*]特性。

[*]在类型的"可变数组性"没有丢失的情况下变得重要,这种情况出现在2D VLA(或指向VLA的指针)中。例如,定义为

int sum2d(int n, int m, int a[n][m])
{
  ...
}

可能被原型化为以下任何一种

int sum2d(int, int, int a[*][*]);
int sum2d(int n, int, int a[n][*]);
int sum2d(int, int m, int a[*][m]);
int sum2d(int n, int m, int a[n][m]);

以上所有的原型都与函数定义相匹配。

当然,如果您有始终命名函数原型中所有参数的习惯,那么您将永远不需要使用此[*]语法,因为您将能够使用上述列表中的最后一个原型。

P.S. 同所有参数声明中的数组一样,第一个[]总是无关紧要的并且总是会衰减为指针,这意味着以下也是针对上述sum2d有效的等效原型声明。

    int sum2d(int, int, int a[][*]);
    int sum2d(int, int, int (*a)[*]);
    int sum2d(int n, int m, int (*a)[m]);

重要的是第二个[],它必须声明为“可变长度”。


3
有时在原型中无法轻松命名参数。在编写库头文件时,您希望避免与预处理宏中用户使用的名称发生冲突的名称。因此,您必须省略名称或进入保留的名称空间(与实现合作),或者让库保留部分名称空间(例如,使用前缀“MyLibrary_”)。后两种方法可能会变得混乱。 - Eric Postpischil
@AndreyT:请详细说明您在回答中提到的语句“[*]在类型的‘可变数组性’没有丢失的情况下变得重要,这种情况在2D VLA中会出现。”。 - haccks
@EricPostpischil; 它超出了范围 :( - haccks
2
@haccks:我指的是数组参数声明中第一个[]总是丢失的事实。该参数会衰变为指针。这适用于所有数组参数声明,无论它们是否为VLA。 (我在答案中已经说明了这一点)。在C中,“注入”永久的非消失性数组特性的唯一方法是将其作为指向数组的指针,例如int(* p)[5]int(* p)[n]。在参数声明中,这等同于2D数组声明。 - AnT stands with Russia
在变量数组的情况下,比如 int arr[n][m],编译器需要知道大小 m,因为在像 arr[2][3] 这样的数组访问中,它必须内部转换为类似于 *(arr + 2 * m + 3) 的东西(注意表达式中的 m)。在 ANSI C 中,您唯一的选择是传递 int *arr 并手动执行 *(arr + 1 * m + 1) 部分,但在 C99 中,您可以让编译器为您处理,因此有了新的语法。第一个大小(在本例中为 n)从未被需要,因为它们的元素总是紧挨着彼此(在这种情况下为 sizeof int 字节)。 - yyny

4
当您在实际函数中放置星号时,会出现以下错误:test.c:3: error: ‘[*]’ not allowed in other than function prototype scope。经过一些调查,这实际上是一种在函数原型中声明VLA的方法,使用星号代替变量名。VLA。问题在于,如果您将变量放入VLA而不是星号,它将告诉您该变量未声明,因此星号是c99内置的解决方法。

@AlejandroIván 不,那是不同的语法。 - user529758
1
奇怪...似乎C99允许以这种方式声明可变长度数组。 - Alejandro Iván
@AlejandroIván 看看新帖子。 - aaronman
@H2CO3;这是什么语法?太令人困惑了。如果您知道,请回答一下。 - haccks
2
@haccks 整数数组:int arr[N]; 指向整数的指针数组:int *arr[N]; - 指向整数数组的指针:int (*ptr)[N],可变长度数组:int arr[<非常量表达式>]; - 函数参数列表中的VLA:与之前相同或 int arg[*]; - user529758

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接