为啥对函数的 VLA 数组参数使用星号“[*]”而不是整数?

Posted

技术标签:

【中文标题】为啥对函数的 VLA 数组参数使用星号“[*]”而不是整数?【英文标题】:Why use an asterisk "[*]" instead of an integer for a VLA array parameter of a function?为什么对函数的 VLA 数组参数使用星号“[*]”而不是整数? 【发布时间】:2013-06-26 15:16:37 【问题描述】:

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

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

很容易理解第一个参数(n)指定了第二个参数(a)的长度。但是遇到了另一个用于 VLA 作为参数的原型

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

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

【问题讨论】:

你检查了吗int sum(int n, int a[]); y@aaronman;是的。它的 c99 功能。 所以... int a[*] 是指针数组还是变长数组?光看代码就不简单了……?我的意思是......“int a[*]”与“int *a[]”相同还是只是“int a[]”? Related/Duplicate question。请参阅 Jens Gustedt 的回答。 @hacks 如果您阅读了他回答的结尾,他基本上解释了 AndreyT 在下面解释的内容,即如果您不在函数原型中命名前面的长度参数,[*] 很有用。 【参考方案1】:

[*] 语法旨在用于声明函数原型。这里的关键细节是在函数原型中你不需要命名参数,你只需要指定每个参数的类型。

在您的示例中,如果您保留第一个参数未命名,那么显然您将无法在第二个(数组)参数声明中使用n。然而,在许多情况下,您必须告诉编译器某些参数是 VLA。这就是 [*] 语法来救援的时候。

在您的情况下,如果您省略参数名称,原型可能看起来像

int sum(int, int [*]);

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

int sum(int, int []);

或作为

int sum(int, int *);

并且原型仍将发挥其作用,即它将正确匹配函数定义。换句话说,声明为一维数组的参数的 VLA 属性完全无关紧要,并且此类 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]);

以上所有原型都正确匹配函数定义。

当然,如果你有在函数原型中总是命名所有参数的习惯,那么你将永远不需要这种[*] 语法,因为你将能够使用上面列表中的最后一个原型。

附:同样,与参数声明中的所有数组一样,第一个 [] 始终是无关紧要的,并且始终衰减为指针,这意味着以下也是上述 sum2d 的有效等效原型声明

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

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

【讨论】:

有时你不能轻易地命名原型中的参数。在编写库头时,您希望避免可能与库用户在预处理器宏中使用的名称发生冲突的名称。因此,您必须省略名称或进入保留的名称空间(与实现合作)或让库保留名称空间的一部分(如“MyLibrary_”之类的前缀)。后两者可能会变得丑陋。 @AndreyT;请在您的回答中详细说明您的陈述The [*] becomes important in situations when the "variable arrayness" of the type is not lost, as would be the case with 2D VLA. @EricPostpischil;它越过头顶:( @hacks:我指的是数组参数声明中的第一个[] 总是丢失。参数衰减为指针。这适用于所有数组参数声明,无论它们是否为 VLA。 (我在答案中说明了这一点)。在 C 中将“永久不消失的数组”“注入”到类型中的唯一方法是使其成为指向数组的指针,例如 int (*p)[5]int (*p)[n]。在参数声明中,这相当于 2D 数组声明。跨度> @hacks 对于像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 字节)。【参考方案2】:

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

【讨论】:

@AlejandroIván Nah,这是一种不同的语法。 奇怪...似乎 C99 允许以这种方式声明可变长度数组。 @AlejandroIván 看新帖 @H2CO3;这是哪种语法?它太令人困惑了。如果你知道,请给出答案。 @hacks ints 的数组:int arr[N]; 指向 int 的指针数组:int *arr[N]; -pointer-to-array-of-ints:int (*ptr)[N],变量长度数组:int arr[<non-const expression>]; - 函数参数列表中的 VLA:相同或 int arg[*];

以上是关于为啥对函数的 VLA 数组参数使用星号“[*]”而不是整数?的主要内容,如果未能解决你的问题,请参考以下文章

如果存在 VLA,为啥仍然需要 malloc? [复制]

python俩个星号怎么表示根号二

当我在 Visual Studio 中生成以二维数组作为参数的构造函数时,这些星号是啥?

在 MS Visual C++ 中启用 VLA(可变长度数组)?

使用pybind11开发python扩展库

固定大小的数组与 alloca(或 VLA)