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

时间:2016-03-04 21:51:14

标签: c arrays gcc alloca gcc-extensions

我使用了一个很好的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返回类型将依赖于它的参数或局部变量。 然而,由于我们仍然无法定义此功能,所以这一切都是理论上的。

3 个答案:

答案 0 :(得分:2)

  

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

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

你不想使用第一个,但第二个是自动管理:无论你分配什么&#34;内部&#34;某些功能将会消失#34;当该函数返回时。

唯一的例外是 - 当然 - 返回值。但是,这并没有多大帮助,因为为了留在堆栈中,它的内存(如果通过堆栈返回)是&#34;分配在&#34;函数调用的参数。因此,在调用函数之前必须知道它的大小(否则,人们不知道存储参数的位置,函数的局部变量等等。)

因为&#34;在堆栈上分配&#34;与&#34基本相同;将一些指针推进已知的字节数&#34;这里有一个矛盾:你想在函数内部分配,但在进入函数之前需要知道多少。

这不会奏效。

答案 1 :(得分: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

答案 2 :(得分:0)

到目前为止,我得到的最接近的是利用调用约定(在GCC下工作)-因此,我们具有generator函数,它将生成数组大小-前一半和后一半填充返回的数组:

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