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


    引言

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

    DEMO(main.cpp)

    #include 
    #include 
    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
    

    反编译

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

    AT&T(test.S)

    • 由于是c++代码,所以汇编文件比较大
    • 为了方便阅读,此处仅摘抄重点部分
    00000000000007aa <_Z2t1Ri>:
    #include 
    #include 
    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 
    : 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 828: e8 43 fe ff ff callq 670 <__stack_chk_fail@plt> 82d: c9 leaveq 82e: c3 retq

    初步结论

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

    完善DEMO

    #include 
    #include 
    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
    
    • 可以看到指针甚至在效率上高于引用
    • 当然这是由于误差导致的...

    最终结论

    • 指针与引用在运行效率上是不分伯仲的
    • 喜欢用指针还是引用完全凭借个人喜好
    • 指针在使用的灵活度上具有很高的优势,但如果使用过程中不注意细节,就会存在安全隐患
    • 引用由于受到c++语法的限制,牺牲了一定的灵活性,但却大大提高了使用过程中的安全性
    • 至于网络上说引用更具有运行效率,或许是因为指针在使用前一般会去判断非NULL吧...
  • 相关阅读:
    斯坦福机器学习 Lecture1 (机器学习,监督学习、回归问题、分类问题定义)
    【Linux】进程地址空间
    contos7 设置mongodb需账号密码访问
    华大基因肿瘤检测助力早期癌症筛查,提高癌症患者生存率
    网络面试-0x03http有哪些常见的请求头以及作用
    (十一)Python模块和包
    [计算机入门] IP地址的查询和设置
    resubmit 渐进式防重复提交框架简介
    仓颉语言HelloWorld内测【仅需三步】
    作业day6
  • 原文地址:https://www.cnblogs.com/KBin/p/17668171.html