引言
- 引用是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.cppg++ -g -o test ./main.cppg++ -g -o test ./main.cpp
反编译
objdump -S ./test > ./test.Sobjdump -S ./test > ./test.Sobjdump -S ./test > ./test.S
AT&T(test.S)
- 由于是c++代码,所以汇编文件比较大
- 为了方便阅读,此处仅摘抄重点部分
00000000000007aa <_Z2t1Ri>:#include <iostream>#include <cstring>void t1(int &b){7aa: 55 push %rbp7ab: 48 89 e5 mov %rsp,%rbp7ae: 48 89 7d f8 mov %rdi,-0x8(%rbp)++b;7b2: 48 8b 45 f8 mov -0x8(%rbp),%rax7b6: 8b 00 mov (%rax),%eax7b8: 8d 50 01 lea 0x1(%rax),%edx7bb: 48 8b 45 f8 mov -0x8(%rbp),%rax7bf: 89 10 mov %edx,(%rax)return;7c1: 90 nop}7c2: 5d pop %rbp7c3: c3 retq00000000000007c4 <_Z2t2Pi>:void t2(int *c){7c4: 55 push %rbp7c5: 48 89 e5 mov %rsp,%rbp7c8: 48 89 7d f8 mov %rdi,-0x8(%rbp)++*c;7cc: 48 8b 45 f8 mov -0x8(%rbp),%rax7d0: 8b 00 mov (%rax),%eax7d2: 8d 50 01 lea 0x1(%rax),%edx7d5: 48 8b 45 f8 mov -0x8(%rbp),%rax7d9: 89 10 mov %edx,(%rax)return;7db: 90 nop}7dc: 5d pop %rbp7dd: c3 retq00000000000007de <main>:int main(){7de: 55 push %rbp7df: 48 89 e5 mov %rsp,%rbp7e2: 48 83 ec 10 sub $0x10,%rsp7e6: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax7ed: 00 007ef: 48 89 45 f8 mov %rax,-0x8(%rbp)7f3: 31 c0 xor %eax,%eaxint a = 100;7f5: c7 45 f4 64 00 00 00 movl $0x64,-0xc(%rbp)t1(a);7fc: 48 8d 45 f4 lea -0xc(%rbp),%rax800: 48 89 c7 mov %rax,%rdi803: e8 a2 ff ff ff callq 7aa <_Z2t1Ri>t2(&a);808: 48 8d 45 f4 lea -0xc(%rbp),%rax80c: 48 89 c7 mov %rax,%rdi80f: 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),%rdx81d: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx824: 00 00826: 74 05 je 82d <main+0x4f>828: e8 43 fe ff ff callq 670 <__stack_chk_fail@plt>82d: c9 leaveq82e: c3 retq00000000000007aa <_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 retq00000000000007aa <_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 ./testreal 0m18.444suser 0m18.391ssys 0m0.036skbin@kbin-virtual-machine:~/test$ time ./test preal 0m18.173suser 0m18.141ssys 0m0.016skbin@kbin-virtual-machine:~/test$ time ./testreal 0m18.424suser 0m18.418ssys 0m0.000skbin@kbin-virtual-machine:~/test$ time ./test preal 0m18.261suser 0m18.156ssys 0m0.088skbin@kbin-virtual-machine:~/test$ time ./testreal 0m18.470suser 0m18.429ssys 0m0.028skbin@kbin-virtual-machine:~/test$ time ./test preal 0m18.300suser 0m18.282ssys 0m0.008skbin@kbin-virtual-machine:~/test$ time ./testreal 0m18.434suser 0m18.402ssys 0m0.028skbin@kbin-virtual-machine:~/test$ time ./test preal 0m18.283suser 0m18.259ssys 0m0.008skbin@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.008skbin@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吧…
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END