19

我是 C 新手,有一件事我无法理解。当函数返回不大于寄存器的东西时——我的编译器将它放在 EAX 中。当我返回大结构(不是指针而是结构本身)时——它是通过堆栈返回的。

我的问题是:编译器如何知道如何调用另一个对象导出的函数?有一个调用约定(如 stdcall),但它是关于传递参数,而不是读取返回值,对吗?

应该有一些规则,例如“如果声明的返回值大于 EAX,则从 [bp-...] 获取它”。

还有一个:是否应该说我想要返回的大于寄存器的对象应该存储在堆中并通过指针返回以防止所有操作而不是堆栈操作?

谢谢。

4

2 回答 2

19

返回值传递给调用者的方式也是函数调用约定的一部分。见这里

例如,关于cdecl

许多 C 系统将cdecl调用约定用于 x86 架构。在cdecl中,函数参数按从右到左的顺序压入堆栈。函数返回值在 EAX 寄存器中返回(浮点值除外,它在 x87 寄存器 ST0 中返回)。

[...]

的解释有一些变化cdecl,特别是在如何返回值方面。因此,为不同操作系统平台和/或由不同编译器编译的 x86 程序可能不兼容,即使它们都使用cdecl约定,不要调用底层环境。一些编译器在 EAX:EDX 中返回长度不超过 2 个寄存器的简单数据结构,而需要异常处理程序(例如,定义的构造函数、析构函数或赋值)特殊处理的较大结构和类对象在内存中返回。为了传递“在内存中”,调用者分配内存并将指向它的指针作为隐藏的第一个参数传递;被调用者填充内存并返回指针,返回时弹出隐藏指针。

如果您在堆上分配内存,堆栈操作将比所需的堆操作快得多,因此堆栈总是更快。(在 C 中)您可能想要返回指向堆上某物的指针的唯一原因是因为它不适合堆栈。

澄清:

在上面的最后一句中,“你可能想要的唯一原因......”不应解释为“通常没有理由返回指针”。相反,我的意思是“如果你可以在不返回指针的情况下做你需要的事情,那么决定使用指针的唯一原因是......”。

当然,正如 Chris 在他自己的回答中所说,从函数返回指针有很多正当理由,但我只是在谈论不需要这样做的情况。

换句话说,尽可能按值返回;必须时使用指针

于 2011-03-20T01:22:49.430 回答
4

还有一个问题:是否应该说我想要返回的大于寄存器的对象应该存储在堆中并通过指针返回以防止所有操作而不是堆栈操作?

也许。老实说,选择“指针返回”或“值返回”可能比“我希望返回更快”更有理由做出选择。例如,对于大型对象,通过指针返回比通过堆栈返回更快,但这并没有考虑到与堆栈相比在堆上分配对象所需的时间更多。

更重要的是,指针返回允许您拥有不透明的指针、可变大小的对象和一定程度的多态行为,这些在堆栈对象中是不可能的。如果您想要或需要这些行为,则无论如何都应该使用按指针返回。如果不这样做,您可以使用按值返回,或者您可以将指向用户分配的对象(无论他们喜欢如何)的指针作为参数传递并在您的函数中修改该参数(这有时称为“ out 参数”或类似的东西)。

根据您的需要和代码的功能选择返回方法,而不是您认为更快的方法。如果你发现你绝对需要速度(在 profiling 并发现返回是一个瓶颈之后),然后担心这种微优化。

于 2011-03-20T01:32:06.587 回答