主流编译器将通过引用的基本类型转换为逐个拷贝吗? [英] Do mainstream compilers convert passed-by-reference basic types into pass-by-copy?
问题描述
通过引用传递对象是一种更简单,更快,更安全的方法来传递地址。
现在,基本类型如 int
?将地址传递到 int
并在函数内部使用它将比通过复制传递它慢,因为指针需要在使用之前解除引用。
现代编译器如何处理这个?
int foo(const int& i)
{
cout<<一世; //做任何只读与i。
}
我可以相信他们将这个编译成这个吗?
int foo(const int i)
{
cout<一世;
}
顺便说一下,在某些情况下, code> i 和& i
,然后使用 i
和 * i
用于写入。
int foo int * ptr_i)
{
cout<<一世; //没有解引用,因此更快(?)
//更多的只读操作与i。
* ptr_i = 123; Visual Studio 2010(Express)是一个基于Visual Studio 2010(Visual Studio 2010)的开发框架,在简单的情况下,我至少测试过。任何人测试gcc?
我测试了以下内容:
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 = ok1(i);
i = ok2(i);
std :: cout<<一世;
}
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是完全相同的,毫无疑问已经执行了优化。
2。传递 i
和& i
:
让我们在这里考虑@newacct的解析器。
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 = ok1(i,pi);
// i = ok2(i,pi);
std :: cout<<一世;
}
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
我看不到它写入 pi
。
使用 ok2 $
可能是因为该函数的结果会覆盖内存位置。 c $ c>,编译器就像我预期的一样聪明。它理解 i
和 pi
指向同一个地方,因此它使用硬编码 2
注意:
- 一旦取消注释
ok2
,我已经编译了两次测试,一次取消注释 ok1
- 我在数组中添加了一个查找
vars
,因为对 sqrtl
的简单调用被简化为基本的ADD和MUL类似的操作,而没有实际的调用
- 在发布中编译
- 产生预期结果,当然
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.
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;
}
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) does, in the simple cases I've tested at least. Anyone to test gcc?
I've tested the following:
1. Passing only 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;
}
The 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)
Well, the ASMs are identical, no doubt the optimization was performed.
2. Passing i
and &i
:
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;
}
The 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)
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.
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.
Notes:
- I've compiled twice for both test, once uncommenting only
ok1
, once uncommenting only ok2
. 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 to sqrtl
were simplified into basic ADD- and MUL-like operations without the actual call
- Compiled in Release
- Yielded the expected results, of course
这篇关于主流编译器将通过引用的基本类型转换为逐个拷贝吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!