You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C/C++函数返回值存储位置探究:结构体与类返回时的存储机制

C++函数返回类/结构体时,返回值存在哪儿?

嘿,这个问题问到点子上了!很多人从C转C++都会疑惑这个——毕竟C里基本类型靠%eax这类寄存器返回,那带自定义构造、析构的类/结构体咋整?咱们结合你的代码,把底层逻辑掰扯清楚:

首先得明确:这个问题没有统一的标准答案,它完全取决于你用的CPU架构(比如32位x86还是64位x86-64)、编译器的调用约定(简单说就是编译器和CPU约定好的函数调用规则,比如参数怎么传、返回值怎么存),还有编译器的返回值优化(RVO)——这个优化会直接改变内存操作的逻辑,也是实际开发里最常见的情况。

先看你的Student示例代码

你的Student类只有一个int成员,还有自定义构造和析构函数,咱们分两种核心场景说:

场景1:禁用返回值优化(比如用-fno-elide-constructors编译)

这是最“原汁原味”的调用逻辑,没有编译器优化干扰:

  • 调用者main会先在自己的栈帧里,给student1提前分配好一块内存。
  • 然后main会把这块内存的地址作为隐藏参数传给myFunc(比如32位下用栈传,64位Linux下用%rdi寄存器传)。
  • myFunc拿到这个地址后,会直接在这块内存上构造Student(123)对象——调用构造函数把m_id设为123。
  • 完成后myFunc直接返回,main里的student1就已经是构造好的对象了。

说白了,这时候myFunc就是直接往main的栈上写数据,只是地址是main提前给好的。

场景2:启用返回值优化(默认编译器都会开这个)

这是实际开发中最常见的情况,编译器会直接“偷懒”优化掉不必要的临时对象:

  • 编译器会直接让myFunc在main栈帧里student1的位置上构造Student(123),完全跳过任何临时对象的创建和拷贝步骤。
  • 这时候myFunc的行为就是实打实直接写main的栈,效率和你直接在main里写Student student1(123);几乎一模一样。

不同架构/调用约定的细节差异

上面说的是通用逻辑,不同平台还有更细的规则:

  • 32位x86系统:如果返回的类/结构体大小不超过4字节(比如你的Student),有些调用约定会像C一样用%eax寄存器返回;如果超过4字节,就用上面说的“调用者提前分配内存+传地址”的方式。
  • 64位x86-64系统:
    • Linux/macOS用的System V约定:如果返回的对象大小不超过16字节,且类型支持(比如你的Student),会直接用%rax寄存器返回对象内容;如果超过16字节,还是调用者分配内存传地址。
    • Windows用的MSVC约定:不管对象大小,只要返回的是类/结构体,调用者都会提前分配好内存,把地址作为隐藏参数传给被调用函数,被调用函数直接在该地址构造对象。

最后回答你的两个小问题

  • student1怎么拿到myFunc的返回值?要么是myFunc直接在student1的栈位置构造对象(优化后),要么是myFunc在main提前分配好的内存上构造对象(无优化时),本质都是直接操作main栈里的空间。
  • myFunc是否直接写main的栈?优化开启的情况下,是的;无优化时,本质也是写main栈上的预留空间,只是逻辑上多了一层“按地址写入”的步骤。

内容的提问来源于stack exchange,提问作者iso532

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:14:45