【问题标题】:Can I return pointer to VLA?我可以返回指向 VLA 的指针吗?
【发布时间】:2015-06-17 00:50:02
【问题描述】:

这样的函数原型在 C 中有效吗?

int (*func())[*];

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

【问题讨论】:

  • 大多数时候,如果你想返回一个指向数组的指针,你真正想做的就是返回一个指向数组第一个元素的指针。使用指向第一个元素的指针,您可以通过 p[5] 获取第 5 个元素;使用指向整个数组的指针,您需要执行 p[0][5](*p)[5]
  • []替换[*]应该足够了
  • 原型与问题有什么关系?看起来您正在尝试声明一个函数指针数组,但这不是问题所在。你想做什么?
  • @harald:这是一种尝试声明一个函数,该函数返回一个指向可变长度整数数组的指针。类似的语法 (int (*func())[5]) 适用于声明一个返回指向固定大小数组的指针的函数。
  • 禁止指向 VLA 的部分可能是 6.7.6.2 §2:如果标识符被声明为具有可变修改类型,则它应 [...] 具有任一块范围或函数原型范围。;不知道有没有更合适的

标签: c arrays pointers language-lawyer c11


【解决方案1】:

来自 C 标准(6.2.1 标识符范围)

  1. ...(函数原型是函数的声明,声明其参数的类型。)

和(6.7.6.2 数组声明符)

  1. ...如果大小是*而不是表达式,则数组类型是未指定大小的变长数组类型,只能 用于具有函数原型范围的声明或类型名称;

所以你不能像你展示的那样指定函数的返回类型。

考虑到 1) 函数可能没有数组的返回类型和 2) 可变长度数组具有自动存储持续时间。因此,如果可以返回这样的数组,则该函数具有未定义的行为。请参阅 6.7.6.2 数组声明符:

2 如果一个标识符被声明为具有可变修改的类型,它 应为普通标识符(如 6.2.3 中定义),没有 链接,并且具有块范围或函数原型范围。 如果 标识符被声明为具有静态或线程的对象 存储时长,不能是变长数组类型。

我知道解决您的问题的两种方法。要么动态分配一个数组并将一个指针返回到它的第一个元素。或者将数组打包到一个结构中。在这种情况下,您可以将函数的整个结构作为右值返回。

【讨论】:

  • 1) 不适用,因为他想返回一个指针,2) 如果返回的数组作为参数传入,则不一定适用
  • @Christoph 为什么当他明确地写一个可变长度数组作为返回类型时,你决定要返回一个指针?因此,您的评论与问题毫无共同之处。
  • 因为问题的标题是Can I return pointer to VLA?(这也是他的声明所做的)
  • @Christoph 首先,该函数没有参数,因此您将参数作为数组引用与问题无关。其次,正如我在我的帖子中所写的那样,VLA(s) 具有原子存储持续时间。因此,返回指向此类数组的指针具有未定义的行为。
  • 其实原型自带了一个空的参数列表,这意味着它需要根据默认参数提升规则调整的未指定数量的参数;但我确实明白你的意思,假设 OP 想要在函数中分配 VLA
【解决方案2】:

您应该返回一个指向不完整数组类型的指针,因为可变长度数组的* 表示法仅在参数列表中有效。

示例原型和函数定义:

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);

请注意,如果函数具有自动存储持续时间,则返回指向已在函数中声明的数组(可变长度或其他)的指针是未定义的行为。这意味着您只能返回指向作为参数传入或动态分配的可变长度数组的指针。

【讨论】:

  • 关于你的最后一段,函数f也可以返回一个指向用malloc分配的VLA的指针,类似于int (*p)[m] = malloc(…);,之后它可以有用地返回p,它会是如果 C 类型系统允许 m 更自动地从 p 的类型转移到接收 f() 的结果的左值,那就太好了。但显然这是不可能的:m 必须重复,这在float (*recipient)[m] = f(); 中暗示的所有错误风险。
  • @PascalCuoq:还请注意,原则上您可以通过使用typedef来避免重复自己
  • 我看不出我想到的情况如何(表示f(size_t m) 创建并初始化一个大小为m 的数组并返回一个指向该数组的指针,该指针应放入“指向大小为m的数组的指针”类型的变量)。
  • @PascalCuoq:我在想我的例子,我可以在其中引入typedef float row_of_m[m] 并以这种方式声明matrixrow(甚至可能添加first_row 的另一个原型使用那种类型?);没错,这对您想到的用例没有帮助;无论如何,我都反对引入这样的 typedef,因为它对可读性有负面影响
【解决方案3】:

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

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

定义后的可变长度数组与常规数组没有什么不同,因此当您尝试返回它时,它会衰减为指针。返回指向局部变量的指针会导致未定义的行为。

【讨论】:

  • 这不是一个返回指向 VLA 的指针的函数。它只返回一个指向整数的指针。这对我没有帮助。
  • 问题并不是说当函数返回时,被指向的 VLA 必须超出范围。
  • @PascalCuoq,是的,但除非你想传入数组并返回指向同一个数组的指针,否则我真的看不到任何其他方式。静态范围内不允许使用 VLA。
  • @harald 查看我对 Christoph 回答的评论。
  • @PascalCuoq,动态分配当然是完全不同的野兽,并且解除了大多数限制。
猜你喜欢
  • 2019-04-20
  • 2017-05-15
  • 2019-06-12
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
  • 2014-02-19
  • 2011-02-01
相关资源
最近更新 更多