关于指针与引用传递的效率问题

引言

  • 引用是C++的特性,指针是C语言的特性
  • 关于这两种特性的运行效率,人云亦云,好多人都说引用传递效率更高
  • 以至于一些面试官在自己都不清楚的前提下面试别人
  • 笔者有幸遇到过,由于看过底层汇编,在面试官对我说引用效率更高的时候,导致我一度怀疑自己的记忆力
  • 下面我们就看看引用在汇编层面与指针有什么区别吧

DEMO(main.cpp)

#include <iostream>
#include <cstring>
void t1(int &b)
{
++b;
return;
}
void t2(int *c)
{
++*c;
return;
}
int main()
{
int a = 100;
t1(a);
t2(&a);
return 0;
}
#include <iostream>
#include <cstring>
void t1(int &b)
{
    ++b;
    return;
}

void t2(int *c)
{
    ++*c;
    return;
}
int main()
{
    int a = 100;
    t1(a);
    t2(&a);
    return 0;
}
#include <iostream> #include <cstring> void t1(int &b) { ++b; return; } void t2(int *c) { ++*c; return; } int main() { int a = 100; t1(a); t2(&a); return 0; }

编译

g++ -g -o test ./main.cpp
g++ -g -o test ./main.cpp
g++ -g -o test ./main.cpp

反编译

objdump -S ./test > ./test.S
objdump -S ./test > ./test.S
objdump -S ./test > ./test.S

AT&T(test.S)

  • 由于是c++代码,所以汇编文件比较大
  • 为了方便阅读,此处仅摘抄重点部分
00000000000007aa <_Z2t1Ri>:
#include <iostream>
#include <cstring>
void t1(int &b)
{
7aa: 55 push %rbp
7ab: 48 89 e5 mov %rsp,%rbp
7ae: 48 89 7d f8 mov %rdi,-0x8(%rbp)
++b;
7b2: 48 8b 45 f8 mov -0x8(%rbp),%rax
7b6: 8b 00 mov (%rax),%eax
7b8: 8d 50 01 lea 0x1(%rax),%edx
7bb: 48 8b 45 f8 mov -0x8(%rbp),%rax
7bf: 89 10 mov %edx,(%rax)
return;
7c1: 90 nop
}
7c2: 5d pop %rbp
7c3: c3 retq
00000000000007c4 <_Z2t2Pi>:
void t2(int *c)
{
7c4: 55 push %rbp
7c5: 48 89 e5 mov %rsp,%rbp
7c8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
++*c;
7cc: 48 8b 45 f8 mov -0x8(%rbp),%rax
7d0: 8b 00 mov (%rax),%eax
7d2: 8d 50 01 lea 0x1(%rax),%edx
7d5: 48 8b 45 f8 mov -0x8(%rbp),%rax
7d9: 89 10 mov %edx,(%rax)
return;
7db: 90 nop
}
7dc: 5d pop %rbp
7dd: c3 retq
00000000000007de <main>:
int main()
{
7de: 55 push %rbp
7df: 48 89 e5 mov %rsp,%rbp
7e2: 48 83 ec 10 sub $0x10,%rsp
7e6: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
7ed: 00 00
7ef: 48 89 45 f8 mov %rax,-0x8(%rbp)
7f3: 31 c0 xor %eax,%eax
int a = 100;
7f5: c7 45 f4 64 00 00 00 movl $0x64,-0xc(%rbp)
t1(a);
7fc: 48 8d 45 f4 lea -0xc(%rbp),%rax
800: 48 89 c7 mov %rax,%rdi
803: e8 a2 ff ff ff callq 7aa <_Z2t1Ri>
t2(&a);
808: 48 8d 45 f4 lea -0xc(%rbp),%rax
80c: 48 89 c7 mov %rax,%rdi
80f: e8 b0 ff ff ff callq 7c4 <_Z2t2Pi>
return 0;
814: b8 00 00 00 00 mov $0x0,%eax
}
819: 48 8b 55 f8 mov -0x8(%rbp),%rdx
81d: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
824: 00 00
826: 74 05 je 82d <main+0x4f>
828: e8 43 fe ff ff callq 670 <__stack_chk_fail@plt>
82d: c9 leaveq
82e: c3 retq
00000000000007aa <_Z2t1Ri>:
#include <iostream>
#include <cstring>
void t1(int &b)
{
 7aa:  55                     push   %rbp
 7ab:  48 89 e5               mov    %rsp,%rbp
 7ae:  48 89 7d f8            mov    %rdi,-0x8(%rbp)
    ++b;
 7b2:  48 8b 45 f8            mov    -0x8(%rbp),%rax
 7b6:  8b 00                  mov    (%rax),%eax
 7b8:  8d 50 01               lea    0x1(%rax),%edx
 7bb:  48 8b 45 f8            mov    -0x8(%rbp),%rax
 7bf:  89 10                  mov    %edx,(%rax)
    return;
 7c1:  90                     nop
}
 7c2:  5d                     pop    %rbp
 7c3:  c3                     retq   

00000000000007c4 <_Z2t2Pi>:

void t2(int *c)
{
 7c4:  55                     push   %rbp
 7c5:  48 89 e5               mov    %rsp,%rbp
 7c8:  48 89 7d f8            mov    %rdi,-0x8(%rbp)
    ++*c;
 7cc:  48 8b 45 f8            mov    -0x8(%rbp),%rax
 7d0:  8b 00                  mov    (%rax),%eax
 7d2:  8d 50 01               lea    0x1(%rax),%edx
 7d5:  48 8b 45 f8            mov    -0x8(%rbp),%rax
 7d9:  89 10                  mov    %edx,(%rax)
    return;
 7db:  90                     nop
}
 7dc:  5d                     pop    %rbp
 7dd:  c3                     retq   

00000000000007de <main>:
int main()
{
 7de:  55                     push   %rbp
 7df:  48 89 e5               mov    %rsp,%rbp
 7e2:  48 83 ec 10            sub    $0x10,%rsp
 7e6:  64 48 8b 04 25 28 00   mov    %fs:0x28,%rax
 7ed:  00 00 
 7ef:  48 89 45 f8            mov    %rax,-0x8(%rbp)
 7f3:  31 c0                  xor    %eax,%eax
    int a = 100;
 7f5:  c7 45 f4 64 00 00 00   movl   $0x64,-0xc(%rbp)
    t1(a);
 7fc:  48 8d 45 f4            lea    -0xc(%rbp),%rax
 800:  48 89 c7               mov    %rax,%rdi
 803:  e8 a2 ff ff ff         callq  7aa <_Z2t1Ri>
    t2(&a);
 808:  48 8d 45 f4            lea    -0xc(%rbp),%rax
 80c:  48 89 c7               mov    %rax,%rdi
 80f:  e8 b0 ff ff ff         callq  7c4 <_Z2t2Pi>
    return 0;
 814:  b8 00 00 00 00         mov    $0x0,%eax
}
 819:  48 8b 55 f8            mov    -0x8(%rbp),%rdx
 81d:  64 48 33 14 25 28 00   xor    %fs:0x28,%rdx
 824:  00 00 
 826:  74 05                  je     82d <main+0x4f>
 828:  e8 43 fe ff ff         callq  670 <__stack_chk_fail@plt>
 82d:  c9                     leaveq 
 82e:  c3                     retq   
00000000000007aa <_Z2t1Ri>: #include <iostream> #include <cstring> void t1(int &b) { 7aa: 55 push %rbp 7ab: 48 89 e5 mov %rsp,%rbp 7ae: 48 89 7d f8 mov %rdi,-0x8(%rbp) ++b; 7b2: 48 8b 45 f8 mov -0x8(%rbp),%rax 7b6: 8b 00 mov (%rax),%eax 7b8: 8d 50 01 lea 0x1(%rax),%edx 7bb: 48 8b 45 f8 mov -0x8(%rbp),%rax 7bf: 89 10 mov %edx,(%rax) return; 7c1: 90 nop } 7c2: 5d pop %rbp 7c3: c3 retq 00000000000007c4 <_Z2t2Pi>: void t2(int *c) { 7c4: 55 push %rbp 7c5: 48 89 e5 mov %rsp,%rbp 7c8: 48 89 7d f8 mov %rdi,-0x8(%rbp) ++*c; 7cc: 48 8b 45 f8 mov -0x8(%rbp),%rax 7d0: 8b 00 mov (%rax),%eax 7d2: 8d 50 01 lea 0x1(%rax),%edx 7d5: 48 8b 45 f8 mov -0x8(%rbp),%rax 7d9: 89 10 mov %edx,(%rax) return; 7db: 90 nop } 7dc: 5d pop %rbp 7dd: c3 retq 00000000000007de <main>: int main() { 7de: 55 push %rbp 7df: 48 89 e5 mov %rsp,%rbp 7e2: 48 83 ec 10 sub $0x10,%rsp 7e6: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 7ed: 00 00 7ef: 48 89 45 f8 mov %rax,-0x8(%rbp) 7f3: 31 c0 xor %eax,%eax int a = 100; 7f5: c7 45 f4 64 00 00 00 movl $0x64,-0xc(%rbp) t1(a); 7fc: 48 8d 45 f4 lea -0xc(%rbp),%rax 800: 48 89 c7 mov %rax,%rdi 803: e8 a2 ff ff ff callq 7aa <_Z2t1Ri> t2(&a); 808: 48 8d 45 f4 lea -0xc(%rbp),%rax 80c: 48 89 c7 mov %rax,%rdi 80f: e8 b0 ff ff ff callq 7c4 <_Z2t2Pi> return 0; 814: b8 00 00 00 00 mov $0x0,%eax } 819: 48 8b 55 f8 mov -0x8(%rbp),%rdx 81d: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx 824: 00 00 826: 74 05 je 82d <main+0x4f> 828: e8 43 fe ff ff callq 670 <__stack_chk_fail@plt> 82d: c9 leaveq 82e: c3 retq

初步结论

  • 我们通过编译与反汇编可以看到
  • 不论指针还是引用,所有汇编代码除了t1,t2地址的不同,可以说没有任何区别
  • 故引用其实就是指针,不过是c++帮你解引用(加了*号)并进行了一定的语法限制
  • 以上汇编中或许有一些我没注意到的细节,欢迎各位大佬在评论区指出

完善DEMO

#include <iostream>
#include <cstring>
void t1(int &b)
{
++b;
return;
}
void t2(int *c)
{
++*c;
return;
}
int main(int argc,char **argv)
{
int a = 100;
long b = 10000000000;
bool ptr = false;
if(argc > 1 && strstr(argv[1],"p")) ptr = true;
if(!ptr) while(--b) t1(a);
else while(--b) t2(&a);
return 0;
}
#include <iostream>
#include <cstring>
void t1(int &b)
{
    ++b;
    return;
}

void t2(int *c)
{
    ++*c;
    return;
}
int main(int argc,char **argv)
{
    int a = 100;
    long b = 10000000000;

    bool ptr = false;
    if(argc > 1 && strstr(argv[1],"p")) ptr = true;

    if(!ptr)   while(--b) t1(a); 
    else    while(--b) t2(&a);

    return 0;
}
#include <iostream> #include <cstring> void t1(int &b) { ++b; return; } void t2(int *c) { ++*c; return; } int main(int argc,char **argv) { int a = 100; long b = 10000000000; bool ptr = false; if(argc > 1 && strstr(argv[1],"p")) ptr = true; if(!ptr) while(--b) t1(a); else while(--b) t2(&a); return 0; }

比对运行效率

  • 考虑到环境因素带来的不确定性,比如cpu降频,其它进程抢占cpu等
  • 故我此处运行了多次,其中带有参数p的是使用的指针,不带有任何参数的是使用的引用
kbin@kbin-virtual-machine:~/test$ time ./test
real 0m18.444s
user 0m18.391s
sys 0m0.036s
kbin@kbin-virtual-machine:~/test$ time ./test p
real 0m18.173s
user 0m18.141s
sys 0m0.016s
kbin@kbin-virtual-machine:~/test$ time ./test
real 0m18.424s
user 0m18.418s
sys 0m0.000s
kbin@kbin-virtual-machine:~/test$ time ./test p
real 0m18.261s
user 0m18.156s
sys 0m0.088s
kbin@kbin-virtual-machine:~/test$ time ./test
real 0m18.470s
user 0m18.429s
sys 0m0.028s
kbin@kbin-virtual-machine:~/test$ time ./test p
real 0m18.300s
user 0m18.282s
sys 0m0.008s
kbin@kbin-virtual-machine:~/test$ time ./test
real 0m18.434s
user 0m18.402s
sys 0m0.028s
kbin@kbin-virtual-machine:~/test$ time ./test p
real 0m18.283s
user 0m18.259s
sys 0m0.008s
kbin@kbin-virtual-machine:~/test$ time ./test 
  real  0m18.444s
  user  0m18.391s
  sys  0m0.036s
kbin@kbin-virtual-machine:~/test$ time ./test p
  real  0m18.173s
  user  0m18.141s
  sys  0m0.016s
kbin@kbin-virtual-machine:~/test$ time ./test 
  real  0m18.424s
  user  0m18.418s
  sys  0m0.000s
kbin@kbin-virtual-machine:~/test$ time ./test p
  real  0m18.261s
  user  0m18.156s
  sys  0m0.088s
kbin@kbin-virtual-machine:~/test$ time ./test
  real  0m18.470s
  user  0m18.429s
  sys  0m0.028s
kbin@kbin-virtual-machine:~/test$ time ./test p
  real  0m18.300s
  user  0m18.282s
  sys  0m0.008s
kbin@kbin-virtual-machine:~/test$ time ./test
  real  0m18.434s
  user  0m18.402s
  sys  0m0.028s
kbin@kbin-virtual-machine:~/test$ time ./test p
  real  0m18.283s
  user  0m18.259s
  sys  0m0.008s
kbin@kbin-virtual-machine:~/test$ time ./test real 0m18.444s user 0m18.391s sys 0m0.036s kbin@kbin-virtual-machine:~/test$ time ./test p real 0m18.173s user 0m18.141s sys 0m0.016s kbin@kbin-virtual-machine:~/test$ time ./test real 0m18.424s user 0m18.418s sys 0m0.000s kbin@kbin-virtual-machine:~/test$ time ./test p real 0m18.261s user 0m18.156s sys 0m0.088s kbin@kbin-virtual-machine:~/test$ time ./test real 0m18.470s user 0m18.429s sys 0m0.028s kbin@kbin-virtual-machine:~/test$ time ./test p real 0m18.300s user 0m18.282s sys 0m0.008s kbin@kbin-virtual-machine:~/test$ time ./test real 0m18.434s user 0m18.402s sys 0m0.028s kbin@kbin-virtual-machine:~/test$ time ./test p real 0m18.283s user 0m18.259s sys 0m0.008s
  • 可以看到指针甚至在效率上高于引用
  • 当然这是由于误差导致的…

最终结论

  • 指针与引用在运行效率上是不分伯仲的
  • 喜欢用指针还是引用完全凭借个人喜好
  • 指针在使用的灵活度上具有很高的优势,但如果使用过程中不注意细节,就会存在安全隐患
  • 引用由于受到c++语法的限制,牺牲了一定的灵活性,但却大大提高了使用过程中的安全性
  • 至于网络上说引用更具有运行效率,或许是因为指针在使用前一般会去判断非NULL吧…

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY0MBR7E' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片