我是C的新手,有一件事我无法理解。 当函数返回不大于寄存器的东西时 - 我的编译器把它放在EAX中。 当我返回大结构(不是指针而是结构本身)时 - 它通过堆栈返回。
我的问题是:编译器如何知道如何调用另一个对象导出的函数? 有一个调用约定(比如stdcall)但它是关于传递参数,而不是读取返回的值,对吗?
应该有一些规则,例如“如果声明的返回值大于EAX,则从[bp -...]获取”。
还有一个:说我想要返回的对象(比寄存器大)应该存储在堆中并由指针返回以防止所有堆栈操作,这是正确的吗?
感谢。
答案 0 :(得分:18)
返回值传递给调用者的方式也是函数调用约定的一部分。请参阅here。
例如,关于cdecl
:
使用
cdecl
调用约定 许多C系统为x86 建筑。在cdecl
中,功能 参数被压入堆栈中 从右到左的顺序。功能返回 值在EAX中返回 寄存器(浮点除外) 值,在x87中返回 注册ST0)。
[...]
有一些变化
cdecl
的解释,特别是 在如何返回值。结果是, x86程序编译为不同的 操作系统平台和/或 不同的编译器可以 不兼容,即使他们都使用cdecl
约定,不要打电话 到底层环境。 一些编译器返回简单数据 结构长度为2 在EAX中注册或更少:EDX,和 更大的结构和类对象 需要特殊待遇 异常处理程序(例如,已定义的 构造函数,析构函数或 赋值)在内存中返回。至 传递“在内存中”,调用者分配 内存并将指针传递给它 隐藏第一个参数;被叫者 填充内存并返回 指针,弹出隐藏的指针 回来时。
如果在堆上分配内存,堆栈操作将比必要的堆操作快得多,因此堆栈总是更快。唯一的原因(在C中)你可能想要返回指向堆上某些东西的指针是因为它不适合堆栈。
<强>澄清:强>
在上面的最后一句中,“你可能想要的唯一理由......”不应被解释为“通常没有理由返回指针”。相反,我的意思是“如果你可以在不返回指针的情况下做你需要的东西,那么决定使用指针的唯一理由就是......”。
当然,有很多正当理由可以回答Chris在其自己的答案中指出函数的指针,但我只是在谈论你不需要 这样做的情况。 / p>
换句话说,尽可能按价值返回;必须时使用指针。
答案 1 :(得分:4)
好吧,也许吧。老实说,“按指针返回”或“按价值返回”的选择是你应该有更好的理由而不是“我希望回报更快”。例如,对于大型对象,通过指针返回比通过堆栈更快,但这并没有考虑到与堆栈相比在堆上分配对象所花费的时间更长。还有一个:说我想要返回的对象(比寄存器大)应该存储在堆中并由指针返回以防止所有堆栈操作,这是正确的吗?
更重要的是,返回指针允许您拥有不透明指针,可变大小的对象以及堆栈对象中不可能的某种程度的多态行为。如果你想要或需要这些行为,你应该使用return-by-pointer。如果不这样做,您可以使用按值返回,或者您可以将指针传递给用户分配的对象(但是他们喜欢)作为参数并在函数中修改该参数(这有时称为“ out参数“或类似的东西。”
根据您的需要和代码的作用选择返回方法,而不是您认为更快的方法。如果你发现你绝对需要速度(在分析并发现返回是一个瓶颈之后),然后担心这种微优化。