主流编译器是否将按引用传递的基本类型转换为按复制传递? [英] Do mainstream compilers convert passed-by-reference basic types into pass-by-copy?
问题描述
通过引用传递对象是一种更简单、更快、更安全的传递地址的方法.但对于大多数编译器来说,都是一样的:引用实际上是指针.
Passing an object by reference is an easier, faster and safer way to pass an address to it. But for most compilers, it's all the same: references are really pointers.
那么像 int
这样的基本类型呢?将地址传递给 int
并在函数内部使用它会比通过复制传递它慢,因为指针需要在使用前取消引用.
Now what about basic types like int
? Passing an address to an int
and using it inside a function would be slower than passing it by copy, because the pointer needs to be dereferenced before use.
现代编译器如何处理这个?
How do modern compiler handle, this?
int foo(const int & i)
{
cout << i; // Do whatever read-only with i.
}
我可以相信他们把这个编译成这个吗?
May I trust them to compile this into this?
int foo(const int i)
{
cout << i;
}
顺便说一句,在某些情况下,通过 i
和 &i
甚至可以更快,然后使用 i
读取, 和 *i
用于书写.
By the way, in some cases it could even be faster to pass both i
and &i
, then use i
for reading, and *i
for writing.
int foo(const int i, int * ptr_i)
{
cout << i; // no dereferencement, therefore faster (?)
// many more read-only operations with i.
*ptr_i = 123;
}
推荐答案
Visual Studio 2010 (Express) 可以,至少在我测试过的简单情况下.有人测试gcc吗?
Visual Studio 2010 (Express) does, in the simple cases I've tested at least. Anyone to test gcc?
我已经测试了以下内容:
I've tested the following:
1.仅传递 i
:
int vars[] = {1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123};
int ok1(const int i){
return sqrtl(vars[i]);
}
int ok2(const int & i){
return sqrtl(vars[i]);
}
void main() {
int i;
std::cin >> i;
//i = ok1(i);
i = ok2(i);
std::cout << i;
}
ASM:
i = ok1(i);
000D1014 mov ecx,dword ptr [i]
000D1017 fild dword ptr vars (0D3018h)[ecx*4]
000D101E call _CIsqrt (0D1830h)
000D1023 call _ftol2_sse (0D1840h)
i = ok2(i);
013A1014 mov ecx,dword ptr [i]
013A1017 fild dword ptr vars (13A3018h)[ecx*4]
013A101E call _CIsqrt (13A1830h)
013A1023 call _ftol2_sse (13A1840h)
好吧,ASM 是相同的,毫无疑问,优化已执行.
Well, the ASMs are identical, no doubt the optimization was performed.
2.传递 i
和 &i
:
2. Passing i
and &i
:
让我们在这里考虑@newacct 的分析器.
Let's consider @newacct 's anser here.
int vars[] = {1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123};
int ok1(const int i, int * pi) {
*pi = 2;
return sqrtl(vars[i]);
}
int ok2(const int & i, int * pi) {
*pi = 2;
return sqrtl(vars[i]);
}
void main() {
int i;
int * pi = &i;
std::cin >> i;
i = ok1(i, pi);
//i = ok2(i, pi);
std::cout << i;
}
ASM:
i = ok1(i, pi);
00891014 mov ecx,dword ptr [i]
00891017 fild dword ptr vars (893018h)[ecx*4] // access vars[i]
0089101E call _CIsqrt (891830h)
00891023 call _ftol2_sse (891840h)
i = ok2(i, pi);
011B1014 fild dword ptr [vars+8 (11B3020h)] // access vars[2]
011B101A call _CIsqrt (11B1830h)
011B101F call _ftol2_sse (11B1840h)
在 ok1
中,我看不到它把 2 写入 pi
.大概是明白内存位置无论如何都会被函数的结果覆盖,所以写也没用.
In ok1
I can't see it writing 2 into pi
. Probably it understands that the memory location will be overwritten by the result of the function anyway, so the writing is useless.
使用 ok2
,编译器和我预期的一样聪明.它明白i
和pi
指向同一个地方,所以它直接使用硬编码的2
.
With ok2
, the compiler is as smart-ass as I expected. It understands that i
and pi
point to the same place, so it uses a hardcoded 2
directly.
注意事项:
- 我为这两个测试编译了两次,一次只取消注释
ok1
,一次只取消注释ok2
.同时编译两个函数会导致两个函数之间更复杂的优化,最终都是内联和混合的 - 我在数组
vars
中添加了一个查找,因为对sqrtl
的简单调用被简化为基本的类似 ADD 和 MUL 的操作,而无需实际调用 - 在发布中编译
- 当然取得了预期的结果
- I've compiled twice for both test, once uncommenting only
ok1
, once uncommenting onlyok2
. Compiling both at the same time leads to more complex optimizations between the two functions, which end up all inlined and mixed up - I've added a lookup in the array
vars
because simple calls tosqrtl
were simplified into basic ADD- and MUL-like operations without the actual call - Compiled in Release
- Yielded the expected results, of course
这篇关于主流编译器是否将按引用传递的基本类型转换为按复制传递?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!