C语言 函数如何实际返回 C 中的结构变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22957175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 10:59:24  来源:igfitidea点击:

How does function ACTUALLY return struct variable in C?

cstruct

提问by gitesh.tyagi

How does a function return value is clear to me, just to kick start:

函数返回值如何对我来说很清楚,只是为了开始:

int f()
{
  int a = 2;
  return a;
}

Now agets the memory in stack and its life-span is within the f()in order to return the value it copies the value to a special register which is read by the caller as it knows that the callee have placed the value for him. (Since the size of return-value-holder special register size is limited that's why we cant return large objects therefore In case of advance languages when we want to return object function actually copies the address of object in heap to that special register)

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

Lets come back to C for a situation when i want to return a struct variable not pointer:

当我想返回一个结构变量而不是指针时,让我们回到 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;
}

Code forked from: https://stackoverflow.com/a/22952975/962545

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

Here is the deal,

这是交易,

Lets start with main, itemsvariable declared but not initialized and then function is called which return initialized structure variable which gets copied to the one in main. Now I am bit blurred to understand how function()returned struct variable itemswhich is not dynamically created(technically not in heap) so this variable's life-span is within the function()body, also size of variable itemcan be huge enough not to fit in special register so why it worked?.(I know we can dynamically allocate item inside function and return the address but I don't want alternative, I am looking for explanation)

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

Question:Although it worksbut how does function()actually returned the struct variable and get copiedto itemsvariable in main when it is supposed to die with function()return.

问题:虽然它有效,function()实际上如何返回结构变量并items在它应该随着function()返回而死亡时被复制到main 中的变量。

I am surely missing important thing, detailed explanation would help. :)

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

EDIT:Other Answer References:

编辑:其他答案参考:

  1. https://stackoverflow.com/a/2155742/962545
  2. Named as return value optimization
  1. https://stackoverflow.com/a/2155742/962545
  2. 命名为返回值优化

采纳答案by nneonneo

Details vary widely by calling convention. Some ABIs have no calling convention for passing whole structures, in which case the compiler is free to do whatever it thinks makes sense.

详细信息因调用约定而异。一些 ABI 没有传递整个结构的调用约定,在这种情况下,编译器可以自由地做任何它认为有意义的事情。

Examples include:

例子包括:

  • Passing and returning the entire struct as a series of consecutive registers (often used with "small" structs)
  • Placing the entire struct as an argument block on the stack
  • Allocating an empty argument big enough to hold the struct, to be filled with a return value
  • Passing the (stack) address of the struct as an argument (as if the function was declared void function(struct inventory *))
  • 将整个结构作为一系列连续的寄存器传递和返回(通常与“小”结构一起使用)
  • 将整个结构作为参数块放在堆栈上
  • 分配一个足够大的空参数来保存结构,用返回值填充
  • 将结构的(堆栈)地址作为参数传递(就像声明了函数一样void function(struct inventory *)

Any of these implementations could conform to the C spec here. But, let's look at a specific implementation: the output from my GCC ARM cross-compiler.

这些实现中的任何一个都可以符合此处的 C 规范。但是,让我们看一个具体的实现:我的 GCC ARM 交叉编译器的输出。

Compiling the code you gave gives me this:

编译你给的代码给了我这个:

main:
    stmfd   sp!, {fp, lr}
    add fp, sp, #4
    sub sp, sp, #48
    sub r3, fp, #52
    mov r0, r3
    bl  function(PLT)

Destination operands are always on the left. You can see that the program reserves stack space, then passes the address of the stack space as r0(the first argument in the ARM EABI calling convention). functiontakes no arguments, so this argument is clearly an artificial argument added by our compiler.

目标操作数总是在左边。可以看到程序预留了堆栈空间,然后将堆栈空间的地址作为r0(ARM EABI调用约定中的第一个参数)传递。function没有参数,所以这个参数显然是我们的编译器添加的人工参数。

functionlooks like this:

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}

This code basically stashes the single argument in [fp, #-40], then later loads it and begins stashing data at the address it points to. At the end, it returns this pointer value in r0again. Effectively, the compiler has made the function signature into

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

struct inventory *function(struct inventory *)

where the returned structure is allocated on the stack by the caller, passed in, and then returned.

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

回答by Elias Van Ootegem

You're missing the most obvious thing there is to C's way of passing/returning things: everything is passed around by value, or at least: it behaves that way.

你错过了 C 传递/返回事物的最明显的事情:一切都是通过 value 传递的,或者至少:它的行为方式是这样。

That is to say:

也就是说:

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

Will work, just fine. If the struct is small, then it's possible that this code will create a local struct variable, and return a copyof that struct to the caller.
In other cases, however, this code will roughly translate to :

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

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;
}

Well, this isn't Exactlywhat happens all of the time, but it boils down to this, more or less. The result will be the same, but compilers may behave differently in this case.

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

Bottom line is: C returns by value, so your code works fine.
However, there are pitfalls:

底线是:C 按值返回,因此您的代码可以正常工作。
但是,也有陷阱:

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

Is dangerous: the pointer assigned to local.strpoints to memory that willbe released once the struct is returned. In that case, the problems you expected with this code are true: that memory is no more (or is not valid anymore).
Simply because a pointer is a variable whose valueis the mem address, and that value is returned/assigned.

是危险的:分配给的指针指向一旦结构体返回就会被释放的local.str内存。在这种情况下,您对这段代码的预期问题是正确的:内存不再存在(或不再有效)。 仅仅因为指针是一个变量,其是内存地址,并且该值被返回/分配。

回答by Adrian Ratnapala

A struct, at least a large one, will be allocated and returned on the stack, and will be popped off the stack (if at all) by the caller. The compiler will try to allocate it in the same spot where the caller is expecting to find this, but it will make a copy if that is not possible. It is possible, but not necessary that there is alsoa pointer to the struct, returned via registers.

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

Of course the details will vary depending on architecture.

当然,细节会因架构而异。