难道主流的编译器转换通过按参考基本类型为通通过拷贝? [英] Do mainstream compilers convert passed-by-reference basic types into pass-by-copy?

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

问题描述

按引用传递的对象是一个地址传递给它一个更简单,更快捷,更安全的方式。
但对于大多数编译器,它都是一样的:引用是真的指针

现在怎么样基本类型,如 INT ?传递一个地址到 INT ,并用它在函数内部会比通过复制传递给它慢,因为指针使用前需要被取消引用。

如何现代编译器手柄,这一点?

  INT美孚(const int的&安培; I)
{
   COUT<<一世; //做任何只读与我。
}

我可以信任他们编译成这样呢?

  INT美孚(const int的我)
{
   COUT<<一世;
}

顺便说一句,在某些情况下,甚至可能会更快地同时通过 I &放大器;我,然后用 I 阅读和 * I 写作。

  INT美孚(const int的我,为int * ptr_i)
{
   COUT<<一世; //没有dereferencement,因此更快的(?)
   //更多只读以i操作。
   * ptr_i = 123;
}


解决方案

Visual Studio 2010中(前preSS)不,在简单情况下,我已经至少进行测试。任何人测试GCC?

我测试过以下内容:

1。只有通过 I

  INT瓦尔[] = {} 1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123;INT OK1(const int的我){
    返回sqrtl商(VAR [I]);
}INT OK2(const int的&安培;我){
    返回sqrtl商(VAR [I]);
}无效的主要(){
    INT I;
    给std :: cin>>一世;
    // I = OK1(ⅰ);
    I = OK2(ⅰ);
    性病::法院LT&;<一世;
}

该ASM:

  I = OK1(I)
000D1014 MOV ECX,DWORD PTR [I]
000D1017 F​​ILD DWORD PTR瓦尔(0D3018h)[ECX * 4]
000D101E调用_CIsqrt(0D1830h)
000D1023调用_ftol2_sse(0D1840h)I = OK2(ⅰ);
013A1014 MOV ECX,DWORD PTR [I]
013A1017 F​​ILD DWORD PTR瓦尔(13A3018h)[ECX * 4]
013A101E调用_CIsqrt(13A1830h)
013A1023调用_ftol2_sse(13A1840h)

那么,ASM的是相同的,无疑是优化进行。

2。通过 I &放大器;我

让我们在这里考虑@newacct的雁。

  INT瓦尔[] = {} 1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123;INT OK1(const int的我,为int * PI){
    * PI = 2;
    返回sqrtl商(VAR [I]);
}INT OK2(const int的&安培;我,INT * PI){
    * PI = 2;
    返回sqrtl商(VAR [I]);
}无效的主要(){
    INT I;
    INT * PI =安培;我;
    给std :: cin>>一世;
    I = OK1(I,PI);
    // I = OK2(I,PI);
    性病::法院LT&;<一世;
}

该ASM:

  I = OK1(I,PI);
00891014 MOV ECX,DWORD PTR [I]
00891017 F​​ILD DWORD PTR瓦尔(893018h)[ECX * 4] //访问瓦尔[I]
0089101E调用_CIsqrt(891830h)
00891023电话_ftol2_sse(891840h)I = OK2(I,PI);
011B1014 FILD DWORD PTR [瓦尔+ 8(11B3020h)] //访问瓦尔[2]
011B101A调用_CIsqrt(11B1830h)
011B101F调用_ftol2_sse(11B1840h)

OK1 我无法看到它写2到 PI 。也许这了解到,内存位置将被函数的结果,反正被覆盖,所以写的是无用的。

使用 OK2 ,编译器是智能屁股如我所料。据了解, I PI 指向同一个地方,所以它采用了硬codeD 2 直接

注:


  • 我为两个测试编了两次,一次取消注释 OK1 ,一旦取消注释仅 OK2 。同时编制都导致了两个函数,这结束了所有内联和混合起来之间更复杂的优化

  • 我在阵列中增加了一个查找瓦尔因为 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天全站免登陆