返回语句表达式的结果存储在内存的哪一部分?

which part of the memory does the result of the expression of the return statements gets stored in?

提问人:aTechieSmile 提问时间:5/9/2022 最后编辑:aTechieSmile 更新时间:5/10/2022 访问量:415

问:

案件是

 int func(void){
        int A = 10;
        int B = 20;
        return A+B
    }

由 main 函数调用

int main(void){
    int retVal = func();
    return 0;
}

在函数中,两个局部变量将存储在 范围的堆栈上,但 A+B 的结果存储在哪里?func()func()

通过引用调用,这种方法的可靠性如何?

以下功能体有什么区别

int func(void){
    int A = 20;
    return A;
}

int* func(void){
    int A = 20;
    return &A;
}

为什么返回值不会抛出分段错误,但返回地址会?

C 函数 返回 按值调用

评论

0赞 Scott Hunter 5/9/2022
“可靠”在什么意义上?
1赞 Some programmer dude 5/9/2022
编译器可能会内联 的代码。它甚至可以在编译时进行计算,因此根本不存储值。在这种特定情况下,由于未使用,编译器很可能根本不会调用该函数,而只是忽略它和变量。funcretValretVal
0赞 aTechieSmile 5/9/2022
@ScottHunter,就像从函数返回局部变量的地址一样,它将是悬空指针,但返回局部变量的值的情况相同,我从未见过任何问题。

答:

1赞 KamilCuk 5/9/2022 #1

A+B的结果存储在哪里?

很大程度上取决于特定的架构和特定的调用召集 - 每个架构都是不同的。让我们来看看最常见的一个 - Linux 上的 x86-64(参见 https://en.wikipedia.org/wiki/X86-64https://en.wikipedia.org/wiki/X86_calling_conventions#cdeclx86-64 System V ABI 在哪里记录 https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture

您提供的函数由 gcc 编译到 godbolt 链接

func:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 10
        mov     DWORD PTR [rbp-8], 20
        mov     edx, DWORD PTR [rbp-4]
        mov     eax, DWORD PTR [rbp-8]
        add     eax, edx
        pop     rbp
        ret

在 x86-64 上,返回值存储在寄存器中。将加法的结果放在寄存器内。设置后,函数比返回,并可以读取寄存器的内容,如果他想要得到返回值。eaxadd eax, edxeaxeaxmaineax

评论

0赞 aTechieSmile 5/9/2022
这是完全可以理解的,但是根据编程标准,是不是不建议返回局部变量的地址,因为悬空指针的问题,那么这个方法直接返回值或表达式怎么可靠呢?
0赞 John Bode 5/9/2022
@geeeeekyDeveloper:此方法直接返回值。它不会返回地址。我不确定我是否理解你的问题。
0赞 aTechieSmile 5/9/2022
@JohnBode我编辑了我的问题,它可能会帮助您正确理解我的问题
0赞 John Bode 5/10/2022 #2

在函数 func() 中,两个局部变量将存储到 func() 作用域的堆栈中,但 A+B 的结果存储在哪里?

取决于目标体系结构的特定调用约定,通常在寄存器中(例如在 x86 上)。eax

以下功能体有什么区别

int func(void){
    int A = 20;
    return A;
}

int* func(void){
    int A = 20;
    return &A;
}

在第一种情况下,你返回表达式的结果,它只是整数值;IOW,该值被写入某个寄存器或其他内存位置,由调用函数读取。A2020

在第二种情况下,您将返回表达式 的结果,即 中变量的地址。这样做的问题是,一旦出口不复存在,并且该内存位置可用于其他内容;指针值不再有效,并且取消引用无效指针的行为未定义。&AAfuncfuncA

1赞 Gem Taylor 5/10/2022 #3

鉴于您用“C”关键字标记了这一点,值得一提的是,C 早期的意图是返回值(作为整数或指针)应该适合处理器寄存器,因此没有分配内存来存储该值。

调用函数可能需要声明一个变量来存储结果,并负责该分配。从函数返回后,调用方会立即将商定的处理器寄存器值存储到它保留的内存中。当然,如果该值立即用于其他计算,则可能没有必要这样做。

当返回指针时,指针指向的内容对程序员来说是一个问题:你。正如你所发现的,如果你试图访问一个值,你只在被调用的函数中声明为局部变量,并使用指针返回,局部变量空间(函数调用堆栈)被大量重用,你的值将很快被丢弃。

当然,您可以在现代 C 和 C++ 中返回浮点值和结构,这通常需要不同的处理。通常,调用方函数必须为被调用函数保留空间,以便将这些较大的对象存储到其中。

请注意,编译器通常能够内联和优化代码以使用可用的寄存器,而不是重复使用商定的寄存器,有时甚至用一组寄存器替换小型结构。

像 godbolt 这样的工具可以让你轻松查看编译器对你的代码做了什么。