函数返回值对我来说很清楚,只是为了开始:
int f()
{
int a = 2;
return a;
}
现在a
获取堆栈中的内存,并且其生存期在f()
内,以便返回该值,它将其复制到一个特殊的寄存器,该寄存器由调用方读取,因为它知道被调用方已放置对他的价值。(由于返回值持有者特殊寄存器的大小是有限的,所以我们不能返回大对象,因此,对于高级语言,当我们要返回对象功能时,实际上会将对象的地址复制到了该特殊寄存器中)
当我想返回结构变量而不是指针时,让我们回到C的情况:
struct inventory
{
char name[20];
int number;
};
struct inventory function();
int main()
{
struct inventory items;
items=function();
printf("\nam in main\n");
printf("\n%s\t",items.name);
printf(" %d\t",items.number);
getch();
return 0;
}
struct inventory function()
{
struct inventory items;
printf(" enter the item name\n ");
scanf(" %s ",&items.name );
printf(" enter the number of items\n ");
scanf("%d",&items.number );
return items;
}
源于https://stackoverflow.com/a/22952975/962545的代码
这是交易,
让我们从main开始,声明items
变量但未初始化,然后调用函数,该函数返回初始化的结构变量,该变量被复制到main中的那个。现在,我有点困惑,无法理解function()
如何返回不是动态创建的结构变量items
(技术上不在堆中),因此该变量的寿命在function()
主体之内,变量item
的大小也可以足够大而不适合特殊寄存器,为什么它起作用?。(我知道我们可以在函数内部动态分配项并返回地址,但我不想替代,我在寻找解释)
问题:虽然是it works,但是当应该以function()
返回时,items
实际上返回了struct变量并被复制转换为main中的function()
变量。
我肯定错过了重要的事情,详细的说明会有所帮助。 :)
编辑:其他答案参考:
细节因调用约定而异。一些ABI没有传递整个结构的调用约定,在这种情况下,编译器可以自由地执行其认为合理的任何操作。
示例包括:
void function(struct inventory *)
)这些实现中的任何一个都可以符合此处的C规范。但是,让我们来看一个具体的实现:我的GCC ARM交叉编译器的输出。
编译您提供的代码给了我这个:
main:
stmfd sp!, {fp, lr}
add fp, sp, #4
sub sp, sp, #48
sub r3, fp, #52
mov r0, r3
bl function(PLT)
目标操作数始终在左侧。您可以看到程序保留了堆栈空间,然后将堆栈空间的地址作为r0
(ARM EABI调用约定中的第一个参数)传递。 function
不带参数,因此此参数显然是我们的编译器添加的人为参数。
function
看起来像这样:
function:
stmfd sp!, {r4, fp, lr}
add fp, sp, #8
sub sp, sp, #36
str r0, [fp, #-40]
ldr r3, .L6
...
add r2, pc, r2
mov r0, r2
mov r1, r3
bl scanf(PLT)
ldr r3, [fp, #-40]
mov ip, r3
sub r4, fp, #36
ldmia r4!, {r0, r1, r2, r3}
stmia ip!, {r0, r1, r2, r3}
ldmia r4, {r0, r1}
stmia ip, {r0, r1}
ldr r0, [fp, #-40]
sub sp, fp, #8
ldmfd sp!, {r4, fp, pc}
此代码基本上将单个参数存储在[fp, #-40]
中,然后在以后加载它,并开始将数据存储在其指向的地址处。最后,它再次在r0
中返回此指针值。有效地,编译器已将函数签名设置为
struct inventory *function(struct inventory *)
返回的结构由调用方分配在堆栈上,传入,然后返回。
您错过了C传递/返回事物的方式中最明显的东西:所有东西都通过值传递或至少:它的行为方式如此。
也就是说:
struct foo some_f( void )
{
struct foo local = {
.member = 123,
.bar = 2.0
};
//some awsome code
return local;
}
会的,很好。如果该结构很小,则此代码可能会创建一个本地结构变量,并将该结构的copy返回给调用方。但是,在其他情况下,此代码将大致翻译为:
void caller()
{
struct foo hidden_stack_space;
struct foo your_var = *(some_f(&hidden_stack_space));
}
//and the some_f function will behave as:
struct foo * some_f(struct foo * local)
{
//works on local and
return local;
}
嗯,这不是精确地 所有时间会发生什么,但是归结为这一点,或多或少。结果将是相同的,但是在这种情况下,编译器的行为可能会有所不同。
最底线是:C按值返回,因此您的代码可以正常工作。但是,有一些陷阱:
struct foo
{
int member1;
char *str;
};
struct foo some_f()
{
char bar[] = "foobar";
struct foo local = {
.member1 = 123,
.str = &bar[0]
};
return local;
}
很危险:分配给local.str
的指针指向一旦返回结构便释放将的内存。在那种情况下,您期望使用此代码的问题是正确的:该内存不再(或不再有效)。仅仅因为指针是一个变量,其value是内存地址,并且该值被返回/分配。
一个结构,至少是一个大的结构,将被分配并返回到堆栈上,并由调用方从堆栈中弹出(如果有的话)。编译器将尝试在调用者希望找到它的位置分配它,但是如果不可能的话,它将进行复制。可能但不一定有通过寄存器返回的指向结构的指针。当然,具体内容将取决于体系结构。