Low level 内存中存储的返回值在哪里?

Low level 内存中存储的返回值在哪里?,low-level,Low Level,内存中存储的返回值在哪里 考虑以下代码: int add(int a, int b) { int result = a+b; return result; } void main() { int sum = add(2, 3); } 调用add(2,3)时,2个函数参数被推送到堆栈上,堆栈帧指针被推送到堆栈上,返回地址被推送到堆栈上。然后执行流跳转到add(…),该函数中的局部变量也存储在堆栈中 当add(…)完成,并执行return指令时。。。返回值存储在哪里?[r

内存中存储的返回值在哪里

考虑以下代码:

int add(int a, int b) {
    int result = a+b;
    return result;
}

void main() {
    int sum = add(2, 3);
}
调用
add(2,3)
时,2个函数参数被推送到堆栈上,堆栈帧指针被推送到堆栈上,返回地址被推送到堆栈上。然后执行流跳转到
add(…)
,该函数中的局部变量也存储在堆栈中


add(…)
完成,并执行
return
指令时。。。返回值存储在哪里?
[result]
如何在
[sum]
中结束?

在x86上,返回值被放入EAX寄存器(这可能取决于您的实际调用约定)


您可以反汇编从源代码编译的代码,以确定发生了什么。

函数参数、局部变量和返回值可以在堆栈上推送/弹出,或者存储到内部CPU寄存器中,这与系统高度相关

通常在蓄能器中。对于不适合累加器的返回值,累加器将在堆栈上保留指向它的指针。这是一种常见的方案,在我在该级别处理的少数平台上使用,但取决于硬件,我认为也取决于编译器/汇编程序。

这显然取决于您的硬件体系结构和编译器。在64位x86上,使用
gcc
,您的代码编译为:

        .file   "call.c"
        .text
.globl add
        .type   add, @function
add:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    %edi, -20(%rbp)
        movl    %esi, -24(%rbp)
        movl    -24(%rbp), %eax
        movl    -20(%rbp), %edx
        leal    (%rdx,%rax), %eax
        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax      ; return value placed in EAX
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   add, .-add
.globl main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $3, %esi
        movl    $2, %edi
        call    add
        movl    %eax, -4(%rbp)      ; the result of add is stored in sum
        leave
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
        .section        .note.GNU-stack,"",@progbits
这里,编译器使用EAX寄存器将
add
的结果传递给调用者


您可以在中阅读x86调用约定。

如果大小允许,EAX用于存储返回值(这里是);调用方的操作(在您的情况下是main)是将EAX内容分配给sum

这个问题没有一般的答案,因为它取决于目标体系结构。任何目标体系结构通常都有一个二进制API规范来定义该规范,编译器会创建根据该规范工作的代码。大多数体系结构都使用寄存器将返回值传回,因为这是最快的方法。当然,只有当值适合寄存器时,这才有可能。如果没有,他们可能会使用寄存器对(例如,一个寄存器中较低的32位,另一个寄存器中较高的32位),或者他们会通过堆栈将其传回。有些架构从不使用寄存器,总是通过堆栈传回。由于调用方必须在调用函数之前创建堆栈帧(此规则有例外,但此处保留默认情况),因此当函数返回给调用方且调用方知道如何访问堆栈帧时,堆栈帧仍然存在,因此调用方必须知道,因为它还必须在返回时清理堆栈帧。在大多数架构中,调用者清理堆栈帧,而不是被调用者,因为调用者知道它通过堆栈传递了多少个参数(例如,对于参数数目可变的C函数),而被调用者不知道(不是在编译时,被调用者可能只在运行时知道),因此,让调用方清理它更有意义。在执行此操作之前,调用方可以读回它希望检索的堆栈帧的任何值

移除标签。这个问题与内存分段无关。根据调用约定和优化设置,可以在堆栈上找到这些参数,也可以在寄存器中找到。(当然,在您的特定示例中,编译器将一起删除返回值,因为所有输入都是编译时常量,因此它将归结为一条带有立即数的移动指令)。@Lundin-谢谢,我对此感到不确定;这只是我在书中读到的关于堆栈上的参数等章节的名称。“累加器”的定义是高度系统特定的。不可能用一般的方式回答这个问题。这个实现不对吗?您正在返回局部变量地址,该地址在您从函数返回时立即从堆栈内存中释放。据我所知,您必须为这种实现从堆中分配内存。OP返回整数,因此寄存器可以容纳这么多(4字节),但是,让我们假设,我想返回8KB的结构?我猜寄存器方法会失败得很厉害,因为它们只能返回在函数堆栈内存中分配的结构变量的地址,一旦我从函数返回,这些结构变量就会被释放。在这种情况下,我命中注定:)@SPSandhu:很抱歉,我没有得到任何答案。@SPSandhu我相信这应该能回答你的问题(来自维基百科的链接文章):
“有些编译器在寄存器对EAX:EDX中返回长度不超过2个寄存器的简单数据结构,而需要异常处理程序进行特殊处理的较大结构和类对象(例如,定义的构造函数、析构函数或赋值)在内存中返回。为了传递“内存中”,调用者分配内存并传递指向它的指针作为隐藏的第一个参数;被调用方填充内存并返回指针,返回时弹出隐藏指针。“