内存中的返回值是存储在内存中的吗?
考虑以下代码:
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]
结束?
答案 0 :(得分:5)
这显然取决于您的硬件架构和编译器。在使用gcc
的64位x86上,您的代码将编译为:
.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
的结果传递给调用者。
您可以在Wikipedia中阅读x86调用约定。
答案 1 :(得分:5)
这个问题没有一般的答案,因为它取决于目标架构。对于任何定义该目标体系结构的目标体系结构,通常都有一个二进制API规范,并且编译器会创建根据此规范工作的代码。大多数架构使用寄存器来传回返回值,因为它是最快的方法。当然,只有当值适合寄存器时才有可能。如果不是,它们可能使用寄存器对(例如,一个寄存器中的低32位,另一个寄存器中的高32位),或者它们将通过堆栈将其传回。有些架构从不使用寄存器,总是通过堆栈传回。由于调用者必须在调用函数之前创建一个堆栈帧(此规则有例外,但让我们保留默认情况),当函数返回调用者并且调用者知道如何访问时,堆栈帧仍然存在它,它必须知道,因为它还必须在返回时清理堆栈帧。在大多数体系结构中,调用者清除堆栈帧而不是被调用者,因为调用者知道它通过栈传递了多少个参数(例如,对于一个接受可变数量参数的C函数),而被调用者则不知道(不是在编译时)时间,被调用者可能只知道在运行时),因此让调用者清理它更有意义。在此之前,调用者可以回读它希望检索的堆栈帧的任何值。
答案 2 :(得分:4)
在x86上,返回值放在EAX寄存器中(可能取决于你的实际调用约定)。
您可以反汇编从源代码编译的代码,以检查确实会发生什么。
答案 3 :(得分:0)
函数参数,局部变量和返回值可以在堆栈中推送/弹出,或者存储在内部CPU寄存器中,它与系统高度相关。
答案 4 :(得分:0)
通常在累加器中。对于不适合累加器的返回值,累加器将在堆栈上保存指向它的指针。这是一个常见的方案,在我在该级别处理的几个平台上使用,但依赖于硬件,我也想在编译器/汇编器上。
答案 5 :(得分:0)
EAX用于存储返回值(如果大小允许)(此处可以);它是调用者的动作(主要在你的情况下)将EAX内容分配给总和