我正在使用一个很好的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
将取决于它的参数或局部变量。然而,这目前都是理论上的,因为我们仍然无法定义这个函数。
[..]我希望 VLA 分配保存在被调用的函数中(并且不使用
malloc
)。
在 C 程序(常见的托管实现)中,动态内存存储只有两个来源:堆和堆栈。
您不想使用第一个,但第二个是自动管理的:当您在某个函数的"内部"分配的任何内容都将在该函数返回时"消失"。
当然,唯一的例外是返回值。但是,这并没有多大帮助,因为为了保留在堆栈上,它的内存(如果通过堆栈返回)被"分配到"函数调用的参数下方。因此,在调用函数之前必须知道它的大小(否则就不知道在哪里存储参数、函数的局部变量等)。
由于"堆栈上的分配"本质上与"将某个指针推进已知数量的字节"相同,因此这里有一个矛盾:您想在函数内部分配,但在进入函数之前需要知道多少。
这是行不通的。
如何返回每个函数实例大小不同的 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("%dn", t.a[i]);
}
// automatic "free"
return 0;
}
输出
0
1
2
3
4
我最接近的是利用调用约定(在 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 函数):
(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]);
}
这是一些现场示例