返回值存储在内存的什么位置?

发布于 2024-10-27 13:34:55 字数 400 浏览 8 评论 0原文

返回值存储在内存的什么位置?

考虑下面的代码:

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] 结束?

Where in memory are return values stored in memory?

Consider the follwing code:

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

void main() {
    int sum = add(2, 3);
}

When add(2, 3) is called, the 2 function parameters are pushed on the stack, the stack frame pointer is pushed on the stack, and a return address is pushed on the stack. The flow of execution then jumps to add(...), and local variables within that function are also stored on the stack.

When add(...) is complete, and executes the return instruction... where does the return value get stored? How does [result] end up in [sum]?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

云朵有点甜 2024-11-03 13:34:55

这显然取决于您的硬件架构和编译器。在使用 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 调用约定。

This clearly depends on your hardware architecture and your compiler. On 64-bit x86 using gcc, your code compiles to:

        .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

Here, the compiler is using the EAX register to communicate the result of add to the caller.

You can read up on x86 calling conventions in Wikipedia.

大海や 2024-11-03 13:34:55

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

There is no general answer to this question, because it depends on the target architecture. There is usually a Binary API Spec for any target architecture that defines that and the compiler creates codes that works according to this spec. Most architectures use a register for passing the return value back, simply because it is the fastest way to do it. That is only possible if the value will fit into a register, of course. If not, they might use a register pair (e.g. lower 32 bit in one register, upper 32 bit in another one), or they will pass it back via the stack. Some architectures never use registers and always pass back via the stack. Since the caller must create a stack frame before calling the function (there are exceptions to this rule, but lets stay with the default case here), the stack frame is still there when the function returns to the caller and the caller knows how to access it, it has to know that, since it must also clean the stack frame on return. On most architectures the caller cleans the stack frame, not the callee, since the caller knows how many arguments it has passed via stack (e.g. for a C function that takes a variable number of arguments), while the callee does not (not at compile time, the callee may only know that at runtime), thus it makes more sense to let the caller clean it. And before doing that, the caller can read back any value of the stack frame it wishes to retrieve.

画▽骨i 2024-11-03 13:34:55

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

您可以反汇编从源代码编译的代码,以检查确实发生的情况。

On x86 the return value is put in the EAX register (that may depend on your actual calling convention though).

You could disassemble the code compiled from your source to check what happens for sure.

野生奥特曼 2024-11-03 13:34:55

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

The function parameters, local variables and return values may be pushed/popped on the stack, or be stored into internal CPU registers, it is highly system dependent.

不气馁 2024-11-03 13:34:55

通常在累加器中。对于无法放入累加器的返回值,累加器将在堆栈上保存指向它的指针。这是一个常见的方案,在我在该级别处理过的少数平台上使用,但取决于硬件,我认为也取决于编译器/汇编器。

Usually in the accumulator. For a return value that does not fit into the accumulator, the accumulator will hold a pointer to it on the stack. This is a common scheme, used on the few platforms I have dealt with at that level, but depends on hardware and I think on the compiler/assembler too.

℡Ms空城旧梦 2024-11-03 13:34:55

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

EAX is used to store the return value if the size permits it (here it does); it's the caller's action (main in your case) to assign the EAX content to sum

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文