2011-03-29 49 views
3

内存中的内存是否存储在内存中的返回值?内存中的内存是否返回存储在内存中的值?

考虑follwing代码:

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

已删除标签。这个问题与内存分割无关。 – Lundin 2011-03-29 11:55:42

+0

根据调用约定和优化设置,您可以在堆栈中找到那些参数或寄存器内的参数。 (当然,在你的具体例子中,由于所有的输入都是编译时常量,所以编译器会将所有返回值一起放回去,所以它会直接放到一个带有立即数的单个移动指令中)。 – Damon 2011-03-29 11:56:53

+0

@Lundin - 谢谢,我觉得那是一个可能;这只是本书中有关堆栈参数的一章的名称。 – 2011-03-29 12:31:46

回答

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调用约定。

+0

是不是这个实现是错误的?当你从函数返回时,你将返回从堆栈内存中释放的局部变量地址。据我所知,你必须从堆中为这种实现分配内存.OP正在返回整数,因此寄存器可以容纳那么多(4字节),但是,可以说,我想返回一个8KB的结构吗?我猜寄存器方法会失败很多,因为它们只能返回获取函数的堆栈内存中分配的结构变量的地址一旦我从函数返回,就会释放它。在这种情况下,我注定要失败:) – 2014-08-04 08:40:02

+0

@SPSandhu:我很抱歉,但是我没有得到任何答案。 – NPE 2014-08-04 09:44:34

+0

@SPSandhu我相信这应该回答您的查询(来自链接的维基百科文章):''一些编译器返回寄存器对EAX:EDX中的长度为2个寄存器或更少的简单数据结构,以及需要特殊的更大结构和类对象在内存中返回异常处理程序的处理(例如,定义的构造函数,析构函数或赋值),为了传递“内存”,调用者分配内存并将指针作为隐藏的第一个参数传递;被调用者填充内存并返回指针,返回时弹出隐藏的指针。“ – forumulator 2017-09-29 15:22:08

3

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

您可以反汇编源代码编译来检查确实发生了什么。

0

函数参数,局部变量和返回值可能在堆栈上被压入/弹出,或被存储到内部CPU寄存器中,这与系统高度相关。

0

通常在累加器中。对于不适合累加器的返回值,累加器将在栈上保存一个指向它的指针。这是一个常用的方案,在我在这个级别处理的少数几个平台上使用,但取决于硬件,我也在编译器/汇编器上进行思考。

+0

“累加器”的定义是高度系统特定的。以通用的方式回答这个问题是不可能的。 – Lundin 2011-03-29 11:57:50

0

EAX用于存储返回值,如果大小允许(在这里它);这是来电者的行动(主要在你的情况)分配EAX内容总和

5

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