2

我正在使用一个不错的 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();

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

4

3 回答 3

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
于 2016-03-05T02:02:50.743 回答
2

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

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

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

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

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

这行不通。

于 2016-03-04T23:44:01.840 回答
0

到目前为止,我得到的最接近的是利用调用约定(在 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]);

}

这是一些活生生的例子

于 2018-11-11T22:04:58.407 回答