我能返回指向VLA的指针吗?

3
这个函数原型在C语言中是否有效?
int (*func())[*];

如果是这样,我该如何定义这些函数?

大多数情况下,如果您认为要返回指向数组的指针,实际上您想要做的是返回指向数组第一个元素的指针。有了指向第一个元素的指针,您可以使用 p[5] 来获取第五个元素;而对于整个数组的指针,您需要执行 p[0][5](*p)[5] - user2357112
[*]替换为[]即可。 - Christoph
原型如何与问题有关?看起来你正在尝试声明一个函数指针数组,但那不是问题所在。你想做什么? - harald
@harald:这是一个尝试声明一个返回指向可变长度整数数组的指针函数。类似语法(int (*func())[5])用于声明返回指向固定大小数组的指针函数。 - user2357112
禁止指向可变长度数组的指针可能在6.7.6.2 §2中:如果标识符被声明为具有可变修改类型,则它应该[...]具有块作用域或函数原型作用域。;不确定是否有更合适的内容。 - Christoph
3个回答

4
你应该返回指向不完整数组类型的指针,因为对于可变长度数组,使用*符号表示法仅在参数列表中有效。
示例原型和函数定义:
extern float (*first_row(unsigned, unsigned, float (*)[*][*]))[];

float (*first_row(unsigned n, unsigned m, float (*matrix)[n][m]))[]
{
    return *matrix;
}

您可以这样调用它:

unsigned n = 3, m = 4;
float matrix[n][m];
float (*row)[m] = first_row(n, m, &matrix);

请注意,如果一个函数内部声明了一个具有自动存储期的数组(可变长度或其他),返回指向该数组的指针是未定义的行为。这意味着您只能返回指向作为参数传递或以动态方式分配的可变长度数组的指针。请注意保留HTML标记。

关于您最后一段,一个函数 f 也可以返回使用 malloc 分配的 VLA 指针,类似于 int (*p)[m] = malloc(…);,之后它可以有用地返回 p,如果 C 类型系统允许将 m 更自动地从 p 的类型传递到接收 f() 结果的 lvalue,那就太好了。但显然这是不可能的:必须重复 m,这意味着在 float (*recipient)[m] = f(); 中存在所有错误风险。 - Pascal Cuoq
@PascalCuoq:还要注意的是,你原则上可以通过使用typedef来避免重复。 - Christoph
我不知道如何处理我所考虑的情况(表达式 f(size_t m) 创建并初始化大小为 m 的数组,并返回指向该数组的指针,应将其放置在类型为“大小为 m 的数组指针”的变量中)。 - Pascal Cuoq
@PascalCuoq:我在考虑我的例子,我本可以引入一个typedef float row_of_m[m],然后以这种方式声明matrixrow(甚至可能使用该类型添加另一个first_row的原型?);你说得对,这对于你考虑的用例没有帮助;无论如何,我反对引入这样的typedef,因为它对可读性有负面影响。 - Christoph

3
从C标准(6.2.1 标识符的作用域)可知:函数原型是一个声明函数的声明,其中声明了其参数的类型。同时在(6 .7.6.2 数组声明符)中也提到,如果大小使用“*”而不是表达式,则数组类型是未指定大小的可变长数组类型,仅能在带有函数原型作用域的声明或类型名称中使用。
因此,您不能以您所展示的方式指定函数的返回类型。请注意,1) 函数可能没有数组的返回类型,2) 可变长度数组具有自动存储持续时间。因此,如果这样的数组可以被返回,函数将具有未定义的行为。详见6.7.6.2 数组声明:
如果将标识符声明为具有可变修改类型,则它应为一个普通标识符(如6.2.3中定义),没有链接,并且具有块作用域或函数原型作用域。如果将标识符声明为具有静态或线程存储期限制的对象,则不应该具有可变长度数组类型。
我了解两种解决方法。一是动态分配数组并返回其第一个元素的指针。另一种是将数组打包在结构体中。在这种情况下,您可以将整个结构体作为rvalue从函数中返回。

4
1)如果他想返回一个指针,则不适用。 2)如果返回的数组作为参数传入,则不一定适用。 - Christoph
1
因为问题的标题是“我可以返回指向VLA的指针吗?”(这也是他的声明所做的)。 - Christoph
@Christoph 首先,该函数没有参数,因此您将参数引用为数组与问题无关。其次,正如我在帖子中所写的那样,VLA具有自动存储期限。因此,返回指向这种数组的指针具有未定义的行为。 - Vlad from Moscow
@FISOCPP 我能想到这样一个函数的唯一用途是分配这种类型的数组或重新分配这样的数组,类似于 mallocrealloc。您需要两个大小参数,并且在重新分配的情况下,需要一个指向1-D数组的指针。换句话说,您已经在调用程序中声明了 int (*array_2D)[X];,并且将其分配给 int (*foo_alloc(size_t m, size_t n))[]; 的结果以分配大小为 m * n 的2-D数组,其中 n 的位置将传递 X。这样一个函数的函数指针将是 int (*(*allocfn)(size_t, size_t))[]; - user539810
为了更容易理解,你可能会使用 typedeftypedef int (*ArrayPtr)[]; ArrayPtr foo_alloc(size_t, size_t); ArrayPtr (*allocfn)(size_t, size_t); - user539810
显示剩余3条评论

-1

从技术上讲,你可以这样做,但这不是一个好主意。

int * func(size_t s)
{
    int array[s];
    return array;
}

在定义变量长度数组后,它与常规数组并无不同,因此在尝试返回它时会衰减为指针。返回指向局部变量的指针会导致未定义行为。

这不是返回VLA指针的函数,而是返回整数指针的函数。这对我没有帮助。 - AnArrayOfFunctions
问题并没有说指向的VLA必须在函数返回时超出范围。 - Pascal Cuoq
@PascalCuoq,没错,但是除非你想传入数组并返回指向同一数组的指针,否则我真的看不到其他方法。 VLAs在静态范围内是不允许的。 - harald
@harald 请看我对Christoph答案的评论。 - Pascal Cuoq
@PascalCuoq,动态分配当然是完全不同的东西,并且解除了大多数限制。 - harald

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