在考虑我们可能不希望总是在多个调用中存储函数的返回值的场景时,请使用以下代码:
int foo()
{
return 1;
}
从以下程序调用。
void main()
{
foo();
}
foo 的返回值会怎样?在没有找到变量地址的情况下,编译器是将它放在备用寄存器上还是在执行下一个语句时被擦除? 如果现在改为将返回值存储在某处:
void main()
{
int retval;
retval = foo();
}
两种情况之间的性能差异是什么?是不是将 retval 存储在哪里会导致堆栈管理出现在轨道上的任何问题?
编辑:自己的配置:工具集:V100,__ cdecl(/ Gd):在VS10中运行Win32编译为C ++ - 内联会发挥作用吗?
我们可以猜测有多少特定配置适用于给定的答案集。一本小书可能就足够了!
至于性能, int 是一个糟糕的选择,也许 float 或 double 会产生更大的影响?
这个问题与提议的副本不同,如下所示:
鉴于foo的返回地址被添加到调用帧中,一种情况是retval的地址放在它下面,但是如果retval是NULL,那么会有额外的开销吗?
答案 0 :(得分:4)
因此,这非常依赖于返回的内容。对于像int
这样的数据类型,大多数实现会将返回值填充到寄存器中,并且调用函数中的优化器会将寄存器视为“脏”,否则将忽略它,除非需要将其重新用于其他内容。基本上,根本没有性能差异。
如果要返回一个具有构造函数和析构函数等的更复杂的值,编译器将不得不调用它们,除非被调用的函数是inline
并且优化器可以确定构造函数和析构函数没有效果。即使它们确实有效,也有一些特定的情况允许编译器忽略构造函数和析构函数调用返回值。 C ++ 11支持移动构造函数已经消除了对此的一些需求。无论如何,这里解决了这个问题:What are copy elision and return value optimization?
对于太大而不适合寄存器的复杂返回值,我看到的实现让调用者为堆栈上的返回值腾出一些空间,并将指向该返回值的指针传入调用函数。这意味着如果调用者可以确定析构函数没有效果,它将简单地省略对它的调用,并将该部分内存视为“脏”以用于优化目的。
答案 1 :(得分:2)
如果您不指定它,它就会丢失。 它取决于调用约定,但通常返回值在寄存器中传递(%rax,例如在SysV x86_64中),如果您不指定它,则只需重用寄存器,这显然会覆盖该值。
在第二种情况下,它会将它放在堆栈上,但优化器很可能会删除代码,因为它没用。但这取决于编译器和优化器。