函数ACTUALLY如何在C中返回struct变量?

git*_*agi 43 c struct

函数返回值如何对我来说是清楚的,只是为了启动:

int f()
{
  int a = 2;
  return a;
}
Run Code Online (Sandbox Code Playgroud)

现在a获取堆栈中的内存并且它的生命周期在其中f()以便返回它将值复制到一个特殊寄存器的值,该寄存器由调用者知道被调用者为他放置了值.(由于返回值持有者特殊寄存器大小的大小是有限的,这就是为什么我们不能返回大对象因此如果我们想要返回对象函数的高级语言实际上将堆中对象的地址复制到该特殊寄存器)

当我想返回一个不是指针的struct变量时,让我们回到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;
}
Run Code Online (Sandbox Code Playgroud)

代码来自:https://stackoverflow.com/a/22952975/962545

这是交易,

让我们从main,items变量声明但未初始化开始,然后调用函数,返回初始化的结构变量,该变量被复制到main中的变量.现在我有点模糊,无法理解function()返回的结构变量items是如何动态创建的(技术上不在堆中)所以这个变量的寿命在function()体内,变量的大小item也足够大,不适合特殊寄存器所以为什么呢工作?(我知道我们可以在函数内部动态分配项目并返回地址,但我不想替代,我正在寻找解释)

问题: 虽然它可以工作但是function() 实际上如何返回struct变量并且items当它应该在function()返回时死亡时被复制到main中的变量.

我肯定错过了重要的事情,详细的解释会有所帮助.:)

编辑: 其他答案参考:

  1. /sf/answers/150901971/
  2. 命名为返回值优化

nne*_*neo 34

通过召集会议,细节差异很大.有些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)
Run Code Online (Sandbox Code Playgroud)

目标操作数始终位于左侧.您可以看到程序保留堆栈空间,然后将堆栈空间的地址作为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}
Run Code Online (Sandbox Code Playgroud)

这段代码基本上存放了单个参数[fp, #-40],然后加载它并开始在它指向的地址存储数据.最后,它r0再次返回此指针值.实际上,编译器已将函数签名放入其中

struct inventory *function(struct inventory *)
Run Code Online (Sandbox Code Playgroud)

返回的结构由调用者在堆栈上分配,传入,然后返回.


Eli*_*gem 10

你错过了C传递/返回事物的方式最明显的事情:一切都是通过值传递的,或者至少是:它表现得那样.

也就是说:

struct foo some_f( void )
{
    struct foo local = {
       .member = 123,
       .bar = 2.0
    };
    //some awsome code
    return local;
}
Run Code Online (Sandbox Code Playgroud)

会工作,就好了.如果结构很小,则此代码可能会创建一个本地结构变量,并将该结构的副本返回给调用者.
但是,在其他情况下,此代码将粗略地转换为:

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;
}
Run Code Online (Sandbox Code Playgroud)

那么,这是不是到底发生了什么,所有的时间,但它归结到这一点,或多或少.结果将是相同的,但在这种情况下编译器可能表现不同.

底线是:C按值返回,因此您的代码工作正常.但是,有一些陷阱:

struct foo
{
    int member1;
    char *str;
};
struct foo some_f()
{
    char bar[] = "foobar";
    struct foo local = {
        .member1 = 123,
        .str = &bar[0]
    };
    return local;
}
Run Code Online (Sandbox Code Playgroud)

是危险的:分配给local.str指向内存的指针的指针在返回结构后释放.在这种情况下,您对此代码所期望的问题是正确的:内存不再存在(或者不再有效).
仅仅因为指针是一个变量,其是mem地址,并返回/分配该值.


Adr*_*ala 5

一个结构,至少是一个大结构,将被分配并返回堆栈,并将由调用者从堆栈中弹出(如果有的话).编译器将尝试在调用者期望找到它的相同位置分配它,但如果不可能,它将进行复制.这是可能的,但不是必需的,有也是一个指针结构,通过寄存器返回.

当然,细节将根据架构而有所不同.