主流编译器将通过引用的基本类型转换为逐个拷贝吗? [英] Do mainstream compilers convert passed-by-reference basic types into pass-by-copy?

查看:118
本文介绍了主流编译器将通过引用的基本类型转换为逐个拷贝吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过引用传递对象是一种更简单,更快,更安全的方法来传递地址。



现在,基本类型如 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆