【发布时间】:2011-07-17 20:09:18
【问题描述】:
正如标题所示
【问题讨论】:
标签: c++ pass-by-reference pass-by-value
正如标题所示
【问题讨论】:
标签: c++ pass-by-reference pass-by-value
编译器供应商通常会将引用实现为指针。指针的大小往往与许多内置类型相同或更大。对于这些内置类型,无论您是按值传递还是通过引用传递,都将传递相同数量的数据。 在函数中,为了获取实际数据,您需要取消引用这个内部指针。这可以向生成的代码添加一条指令,并且您还将拥有 两个 可能不在缓存中的内存位置。差异不会太大 - 但可以在紧密的循环中进行测量。
编译器供应商可以选择在用于内置类型时忽略 const 引用(有时也包括非 const 引用) - 这一切都取决于编译器在处理函数及其调用者时可用的信息.
【讨论】:
对于 int、char、short 和 float 等 pod 类型,数据的大小与传入以引用实际数据的地址相同(或更小)。在引用的地址查找值是不必要的步骤,并且会增加额外的成本。
例如,取如下函数foo和bar
void foo(char& c) {...}
void bar(char c) {...}
当foo 被调用时,地址将通过 32 位或 64 位的值传递,具体取决于您的平台。当您在 foo 中使用 c 时,您需要查找传入地址中保存的数据的值。
当调用bar时传入一个char大小的值,没有地址查找开销。
【讨论】:
char)时,它不是提升为更广泛的类型(16 位)以便通过寄存器传递吗?
在实践中,C++ 实现通常通过在底层传递一个指针来实现传递引用(假设调用不是内联的)。
因此,没有什么聪明的机制可以让按引用传递更快,因为传递指针并不比传递小值快。一旦你在函数中,按值传递也可以从更好的优化中受益。例如:
int foo(const int &a, int *b) {
int c = a;
*b = 2;
return c + a;
}
就编译器所知,b 指向a,这称为“别名”。如果a 是按值传递的,则此函数可以优化为等效于*b = 2; return 2*a;。在现代 CPU 的指令流水线中,这可能更像是“开始 a 加载,开始 b 存储,等待 a 加载,乘以 2,等待 b 存储,返回”,而不是“开始 a 加载,开始 b 存储” ,等待 a 加载,等待 b 存储,开始加载,等待 a 加载,将 a 添加到 c,返回”,然后您开始了解为什么混叠的可能性会对性能产生重大影响。在某些情况下,如果不一定会在这一方面产生巨大影响。
当然,别名仅在它改变函数对某些可能输入的效果的情况下才会阻碍优化。但仅仅因为您对该函数的意图是别名不应影响结果,并不一定意味着编译器可以假设它不会:有时实际上,在您的程序中,不会发生别名,但编译器不会不知道。并且不必有第二个指针参数,只要你的函数调用优化器“看不到”的代码,它就必须假设任何引用都可以改变。
【讨论】: