这是一个很好的问题,因为答案揭示了很多关于幕后发生的事情,尽管它基本上不会影响您编写软件的方式。
首先:答案在很大程度上取决于您使用的 CPU,因为每种架构都有自己的处理寄存器和返回值等的约定。正如上面评论中提到的,“ABI”是应用程序二进制接口定义了一组每个人都必须同意的约定。
ABI 是您深入了解特定平台的方式,而这篇文章充其量只是路过。
对于简单的返回 - 例如一个整数 - 几乎所有 CPU 都有一个“明显”的寄存器用于此,在 Intel x86 平台上它是 eax,对于 x64 它是 rax。
这本质上是许多处理器(至少在概念上)共享的“A”寄存器,并且函数/子例程将返回值存储在此处以供调用者使用。
在这些情况下,调用者在这个“A”寄存器中找到的任何东西都被认为是返回,但它无法知道子程序是否故意在此处放置一个值以进行返回,或者它是否只是计算中的一些随机剩余.
这有一个奇怪的副作用,即一个不会有意返回值的函数在返回时会“返回”eax 寄存器中发生的任何内容。有时这是计算的结果,有时是在返回之前调用的子函数的返回值。
int myfunc()
{
.. do some stuff
int x = 4 * other_function();
// note no "return"
}
可能是计算出的x 被“返回”只是因为它恰好落在了 A 寄存器中,但不要指望它:它本质上是一个错误。它也可能是其他一些值。一个好的编译器会警告你这样的狡猾的事情:关于这些神秘回报的问题一直存在于 SO 上。
当然,这并不总是那么简单:您不能将 128 位值放入 64 位寄存器,因此返回 double(或 long long)的函数必须采取其他规定。
对于整数值,许多架构都同意一起使用两个寄存器,对于浮点值,可以使用一组单独的浮点寄存器,调用者同意查阅。
调用者和被调用者就它们的去向达成一致至关重要。
一种特殊情况是返回一个大项目,例如一个结构。这不是一个好主意,大多数人都不推荐它(尽管合法),而且很明显,返回(比如说)488 字节结构将不适合大多数架构的寄存器。
诀窍是调用者为完整的返回值分配空间,然后将该返回区域的地址在隐藏参数中传递给函数(有时在“A”寄存器中)。被调用者知道将计算结构复制到该隐藏指针指向的内存中。
参考:How do C compilers implement functions that return large structures?
如前所述,这只是路过,您确实必须研究您正在使用的处理器的架构,但这里最重要的事实是:这些是约定,这意味着它们只是每个人都同意的东西,而不是一些固有的“正确”答案。