【问题标题】:How to return VLA with size varying on each function instance?如何返回每个函数实例大小不同的 VLA?
【发布时间】:2016-06-18 19:39:37
【问题描述】:

我正在使用一个不错的 GCC 扩展,它允许我们在结构中声明 VLA。现在我找到了一种通过这种方式将 VLA 传递给函数(按值)的方法。我还找到了一种返回方法,但在非常有限的情况下。

这个例子的功能代码是这样的:

extern void func3()
{
    size_t size;

    scanf("%zu", &size);

    struct tx{int _[size];} fn()
    {
        struct tx rt;

        for(size_t i=0; i < size; ++i)
            scanf("%d", &rt._[i]);

        return rt;
    }

    volatile __typeof__(fn) *pf = fn;
}

上面的例子是为测试目的而设计的(专门用来比较它编译的二进制代码)。

但是这非常有限,因为返回的数组的大小在函数的不同调用之间没有变化。

我怎样才能使返回的数组大小等于函数参数之一或此函数中的某个其他局部参数。

我不认为alloca 在这种情况下可以帮助我,因为它分配的内存会在函数退出 (IRC) 时立即销毁。

我想写这样的东西:

/*???*/ func5()
{
    size_t size;

    scanf("%zu", &size);

    struct {int _[size];} rt;

    for(size_t i=0; i < size; ++i)
        scanf("%d", &rt._[i]);

    return rt; //ok - return the structure
}

换句话说,问号内的类型可能是什么?或者可能有其他解决方案(但不使用malloc)?

这种函数的理论用法理论上需要另一种类型来存储返回值,因为调用者无法获得返回结构的大小(除非有办法避免这种情况?)。但乍一看应该是这样的

size_t size;

//scanf("%zu", &size);

struct {int _[size];} tmp; //create locally VM type 
                            //compatible with the one
                            //returned by our theoretical func5

                            //we can't directly initialize tmp here (gcc complains)


tmp = ((__typeof__(tmp) (*)())func5)(); //direct assignment between VM structures 
                                        //works here on the other hand

                                        //as function return value is rvalue and we can't
                                        //take its pointer and cast it to our local VM structure type
                                        //we instead cast the function pointer

如果我们这样做

__typeof__(func5()) tmp = func5();

它不起作用,因为func5 的 VM 返回类型将取决于它的参数或局部变量。 然而,目前这只是理论上的,因为我们仍然无法定义这个函数

【问题讨论】:

  • 只要让调用者先分配数组,被调用者填入数值即可。
  • @user3528438 我对替代解决方案不感兴趣。我希望将 VLA 分配保留在被调用函数中(并且不使用 malloc)。
  • 我想看看这是如何编码的。喜欢{type_TBD func5(); type_TBD y = func5(); // use y } 吗?你能发布一些使用func5()结果的理论代码吗?
  • 我想说的是,需要知道数组的大小之前 func5() 被调用,以便y = func5()y 可以是声明。
  • 您声称希望 VLA“被保存在被调用的函数中”,但您也想返回它。那必须制作副本,这意味着您需要在调用函数中使用 VLA 来接收副本。 (不管你怎么做,复制是必要的,因为你不能返回一个指向局部变量的指针。)所以唯一的解决方案是@user3528438 建议的那个,当然这也行不通,因为你需要有不同的不同大小的结构的函数版本。简而言之,使用malloc。这就是它的用途。

标签: c arrays gcc alloca gcc-extensions


【解决方案1】:

[..] 我希望 VLA 分配保留在被调用函数中(并且不使用 malloc)。

在 C 程序(常见的托管实现)中,动态内存存储只有两个来源:堆和堆栈。

您不想使用第一个,但第二个是自动管理的:当该函数返回时,您在某个函数“内部”分配的任何内容都将“消失”。

唯一的例外是——当然——返回值。但是,这并没有多大帮助,因为为了留在堆栈上,它的内存(如果通过堆栈返回)被“分配在”函数调用的参数之下。因此,在调用函数之前必须知道它的大小(否则将不知道在哪里存储参数、函数的局部变量等)。

由于“在堆栈上分配”本质上与“将某个指针前进已知字节数”相同,因此这里有一个矛盾:您想在函数内部分配,但在进入函数之前需要知道多少.

这行不通。

【讨论】:

  • 好吧,如果我可以从参数中获取大小(与传递 VLA 相同的方式),也许它会。但是现在这在语法上是不可能的,因为在指定函数返回类型时函数参数不可见。但也许还有其他方法谁知道。
【解决方案2】:

如何返回每个函数实例大小不同的 VLA?

返回 VLA 是一回事,除非它被传入,否则它并不是真正可行的。(那么返回它的意义何在)。除非事先确定其大小,否则调用代码无法接收它。


也许这已经足够接近 OP 的目标了。

使用在知道大小之后但在调用 func5() 之前分配的 VLA。

typedef struct {
  size_t size;
  int *a;
} va;

void func5(va *p) {
  for (size_t i = 0; i < p->size; ++i) {
    // error handling not shown
    // scanf("%d", &p.a[i]);
    p->a[i] = i;
  }
}

int main(void) {

  // create
  size_t size = 5;
  // scanf("%zu", &size);
  int v[size];
  va t = { size, v };

  // populate
  func5(&t);

  // use 
  for (size_t i = 0; i < size; i++) {
    printf("%d\n", t.a[i]);
  }

  // automatic "free"
  return 0;
}

输出

0
1
2
3
4

【讨论】:

  • 恕我直言,这是解决此问题的正确方法,尽管您可能应该摆脱该结构并只传递指针和大小。
【解决方案3】:

到目前为止,我得到的最接近的是利用调用约定(在 GCC 下工作) - 所以我们有生成器函数来生成数组大小 - 前半部分和后半部分填充返回的数组:

returnVLAgenerator(vlaout)
    char vlaout[];
{
    size_t szvla = 3; //calculate vlaout size here

    if(vlaout == 0)
        return szvla;

    while(szvla--) vlaout[szvla] = 'x'; //fill vlaout here

    return;
}

如果你想调用它,你需要生成一个带有函数签名的标记并返回值,如下所示(示例主函数):

(main())
{
    struct { char ar[returnVLAgenerator(0)]}(*returnVLAstamp)() = returnVLAgenerator, vlaOut;

    vlaOut = returnVLAstamp();

    for(size_t i = 0; i < sizeof(vlaOut.ar); ++i)
        printf("%c", vlaOut.ar[i]);

}

这里有一些直播example

【讨论】:

    猜你喜欢
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2016-11-15
    • 2012-01-20
    • 2011-06-17
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多