首页 文章

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

提问于
浏览
41

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

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

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

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

这是交易,

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

Question: 虽然it works但是 function() actually returned the struct variable and get copieditems 变量在主应该死的时候 function() 返回 .

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

EDIT: 其他答案参考文献:

3 回答

  • 33

    通过召集 Session ,细节差异很大 . 有些ABI没有传递整个结构的调用约定,在这种情况下,编译器可以自由地做任何它认为有意义的事情 .

    例子包括:

    • 将整个结构作为一系列连续寄存器传递并返回(通常与"small"结构一起使用)

    • 将整个结构作为参数块放在堆栈上

    • 分配一个足够大的空参数来保存结构,填充返回值

    • 将结构的(堆栈)地址作为参数传递(就好像函数声明为 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 *)
    

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

  • 10

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

    也就是说:

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

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

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

  • 5

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

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

相关问题