变长数组的原型

Posted

技术标签:

【中文标题】变长数组的原型【英文标题】:Prototype for variable-length arrays 【发布时间】:2011-11-05 17:22:14 【问题描述】:

我正在尝试编写一个函数,该函数在 c 中采用可变大小的数组。

void sort(int s, int e, int arr[*])
    ...

它说对于可变长度数组,它需要在函数声明中限定。那是什么意思?我正在使用带有 LLVM 编译器 2.0 的 xcode 4.0。

感谢您的帮助。

【问题讨论】:

这可能会有所帮助:gcc.gnu.org/onlinedocs/gcc/Variable-Length.html 什么是[*]?我以前从未见过。 我可以把它留空 [],但是把 * 让编译器知道它应该使用它后面的参数,即 int e 作为长度。这是我读到的。 @Owen,[*] 更复杂一些。请看我的回答。 【参考方案1】:

因为我看到没有人回答真正的问题,所以我在这里给出我的。

在 C99 中,您有可变长度数组 (VLA),其声明的长度在运行时进行评估,而不仅仅是在编译时与以前版本的 C 一样。但是将数组传递给函数有点棘手。

一维数组总是作为指针传递,所以

void sort(size_t n, int arr[n]) 


等价于

void sort(size_t n, int *arr)


更高的维度很好地传递给函数

void toto(size_t n, size_t m, int arr[n][m])


等价于

void toto(size_t n, size_t m, int (*arr)[m])


通过在这样的函数内部进行这样的定义,您可以使用表达式arr[i][j] 访问元素,并且编译器知道如何计算正确的元素。

现在是你发现的语法,它只对 prototypes 有用,这是你前向声明函数接口的地方

void toto(size_t, size_t, int arr[*][*]);

所以在这里您可以将数组维度替换为* 作为占位符。但这仅在您手头没有维度名称时才有用,并且使用与定义完全相同的版本会更清晰。

void toto(size_t n, size_t m, int arr[n][m]);

一般来说,为了始终如一地使用它,在参数列表中首先拥有尺寸非常重要。否则当编译器解析arr的声明时,它们将不为人所知。

【讨论】:

维度可以放在proto中VLA之后使用args前向声明,即void toto(size_t n, size_t m; int arr[n][m], size_t n, size_t m)跨度> @phi,这是一个非标准的 gnu 扩展 是的,但是对于实现 VLA 的 hte 编译器,除了将暗值沿 &vla[0][0] addr 作为参数传递之外别无选择,然后在堆栈上检索它们是微不足道的,我在两个 gcc 上都进行了测试/clang 并且它们的工作方式相同。对于我的开发人员(仅限 Linux),这个技巧对我来说已经足够好了,我怀疑它很快就会消失。然而这是一个技巧,有一个更“干净”的选项会很棒:)【参考方案2】:

如果您使用 C99 可变长度数组(看起来您是,请参见下文),通常的解决方案是传入指向第一个元素的指针以及任何索引你想用来访问元素。

这是一段代码,它打印出一个数组范围,类似于您尝试对您的 sort 执行的操作。

#include <stdio.h>

static void fn (int *arr, size_t start, size_t end) 
    size_t idx;
    for (idx = start; idx <= end; idx++) 
        printf ("%d ", arr[idx]);
    
    putchar ('\n');


int main (void) 
    int my_array[] = 9, 8, 7, 6, 5, 4, 3, 2, 1, 0;
    fn (my_array, 4, 6);
    return 0;

这会输出元素四到六(从零开始),给出:

5 4 3

有几点需要注意。

在对fn 的函数调用中使用my_array 会自动将数组“衰减”为指向其第一个元素的指针。当您使用数组时,这实际上发生在大多数(不是全部)情况下,因此您不必显式声明 &amp;(my_array[0])

C 已经具有标准库中内置的非常好的排序函数,称为qsort。在许多情况下,这就是您应该使用的(除非您有特定的算法要用于排序,或者您正在做家庭作业/自学练习)。


如果您正在使用真正的 VLA,您应该知道[*] 构造仅在函数原型中有效,在函数的实际定义中无效.

所以,虽然:

void xyzzy(int, int[*]);

有效,以下无效:

void xyzzy(int sz, int plugh[*])  doSomething(); 

这是因为,虽然您在原型中不需要 size 参数,但在定义中却非常需要它。而且,既然你有它,你就应该使用它:

void xyzzy(int sz, int plugh[sz])  doSomething(); 

gcc 编译器实际上对此有相当清晰的错误消息,比您看到的“需要在函数声明中限制”要好得多:

错误:'[*]' 不允许在函数原型范围之外

【讨论】:

嗯,我没有看到你对这个问题给出了有效的答案。您的答案与 VLA 无关,而是一般描述如何将一维数组传递给函数。这只是图片的一半。【参考方案3】:

你想做什么让你的论点成为int *;传入数组的长度(调用者可能知道,但这个例程不知道)作为单独的参数。您可以将数组作为此类参数传递。

【讨论】:

【参考方案4】:

在可变长度数组的数组括号内使用* 仅限于原型,并且仅用作占位符。稍后定义函数时,数组的大小应存储在文件范围内可用的变量中或作为参数之一。这是一个简单的例子:

void foo(int, int[*]);
/* asterisk is placeholder */

void foo(int size, int array[size]) 
/* note size of array is specified now */

【讨论】:

以上是关于变长数组的原型的主要内容,如果未能解决你的问题,请参考以下文章

GCC手册解析——变长数组

变长数组 - 转

C语言怎样定义变长数组

c中可以定义变长数组吗

通过自定义的 Array 类来实现变长数组

通过自定义的 Array 类来实现变长数组