精华内容
下载资源
问答
  • C++ 引用 参数传递 机制

    万次阅读 2016-02-29 23:21:04
    本文主要分析C++引用赋值和引用参数传递的案例。 关于多开GDB,手懒把所有程序都编译成a.out的注意了,gdb中,不确定已经读取文件正在执行过程中会不会产生干扰。至少一次运行结束后,原来断点什么的就不存在了,...

    本文主要分析C++引用赋值和引用参数传递的案例。

     

    关于多开GDB,手懒把所有程序都编译成a.out的注意了,gdb中,不确定已经读取文件正在执行过程中会不会产生干扰。至少一次运行结束后,原来断点什么的就不存在了,文件找不到了。

     

    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    `/home/huqw/test/cpp/a.out' has changed; re-reading symbols.
    Error in re-setting breakpoint 1: No source file named refParamStack.cpp.
    Error in re-setting breakpoint 2: No source file named refParamStack.cpp.
    Error in re-setting breakpoint 3: No source file named refParamStack.cpp.
    Error in re-setting breakpoint 4: No source file named refParamStack.cpp.
    

     

    两个源文件如下,一个swap()函数,分别传入普通参数和引用参数:

     

    //为何引用能改变原变量,看一下函数的栈情况
    #include<iostream>
    #include<stdio.h>
    void swap(int &a,int &b)
    {
            int temp = a;
            a = b;
            b = temp;
    }
    
    int main(){
            int i = 100;
            int j = 200;
            printf("i:%d,j:%d.\n",i,j);
            swap(i,j);
            printf("i:%d,j:%d.\n",i,j);
    }
    

     

    //为何引用能改变原变量,看一下函数的栈情况
    //对比试验,看看正常函数参数的堆栈情况
    #include<iostream>
    #include<stdio.h>
    void swap(int a,int b)
    {
            int temp = a;
            a = b;
            b = temp;
    }
    
    int main(){
            int i = 100;
            int j = 200;
            printf("i:%d,j:%d.\n",i,j);
            swap(i,j);
            printf("i:%d,j:%d.\n",i,j);
    }
    

     

    编译好两个目标文件,打开两个gdb,分别读取两个目标文件,打好断点,同时运行,

     

    如上图,感觉有点串,一个文件的i和另一个文件的j一样,一个文件的j和另一个文件的i一样。。。。两个“例程”的地址已经混淆了?其实这倒也没什么,这只是逻辑地址,而且是静态的,很多文件编译好了变量的地址就不变了,“屡试不爽”了,但是装载到内存,各是各的,不影响。

    继续向下运行,顺便也来验证一下传递引用和传递普通参数的区别。

     

     

    引用方面,函数内外地址一致是肯定的,a、b和i、j对应(至于info frame,目前还看不太好):

    其实即使可执行的目标文件相同,调试打断点和显示代码还是要看源文件,所以最好用两个独立的源文件和两个独立的目标文件,免得产生干扰。

     

    =========================================================================================================================

    @符号的详细解释?不管能不能解释@符号,最起码可以找找相似,既然是@加地址,又因为传指针按理说也是传地址,所以用传指针的情况作对比。

    代码如下:

     

    //对照组:传递指针
    #include<iostream>
    #include<stdio.h>
    void swap(int *a,int *b)
    {
            int temp = *a;
            *a = *b;
            *b = temp;
    }
    
    int main(){
            int i = 100;
            int j = 200;
            printf("i:%d,j:%d.\n",i,j);
            swap(&i,&j);
            printf("i:%d,j:%d.\n",i,j);
    }
    

    gdb调试(函数内部堆栈):

    (gdb) bt
    #0  swap (a=0xbffff60c, b=0xbffff608) at refParamStack3.cpp:7
    #1  0x080485df in main () at refParamStack3.cpp:15
    

     

     

     

    但是指针和引用毕竟不同:层级不同,一个是直接当变量用,一个是指针,需要提取内容。

    把原引用用例再加上普通引用(非函数参数)作为对比:

     

    (gdb) n
    14		int &refI = i;
    (gdb) n
    15		int &refJ = j;
    (gdb) bt
    #0  main () at refParamStack.cpp:15
    (gdb) info locals
    i = 100
    j = 200
    refI = @0xbffff604
    refJ = @0x55fff4
    

     

     

     

    另一个问题是,同一次运行中,出现了不一样的地址,在调用swap()前地址如下

     

    (gdb) info locals
    i = 100
    j = 200
    refI = @0xbffff604
    refJ = @0x55fff4
    

     

    调用中:

     

    (gdb) print a
    $1 = (int &) @0xbffff604: 100
    (gdb) print b
    $2 = (int &) @0xbffff600: 200
    

     

    调用后如下:

    (gdb) info locals
    i = 200
    j = 100
    refI = @0xbffff604
    refJ = @0xbffff600
    

    原因应该是refJ还未定义完成,下一步应该就行了(可见也是分了声明和定义赋值两步)
     

    可见,引用的格式就是@地址,不过具体反映到操作上,@的机制是什么?

     

    =========================================================================================================================

     

    通过汇编指令:

    (gdb)layout asm

    (gdb)si

    查看详细操作

     

    引用初始化的汇编过程示例:

    int &refI = i;

    int &refj = j;

     

    b+ x0x80485af <main()+25>   lea    0x14(%esp),%eax                  x
       x0x80485b3 <main()+29>   mov    %eax,0x18(%esp)                  x
    b+ x0x80485b7 <main()+33>   lea    0x10(%esp),%eax                  x
       x0x80485bb <main()+37>   mov    %eax,0x1c(%esp) 

     

    引用赋值测试:

     

    引用refI指向i,进行如下操作:

    refI = 300;

     

    0x804859f <main()+9>    movl   $0x64,0x14(%esp)                 x
    B+>x0x80485b7 <main()+33>   mov    0x18(%esp),%eax                  x
       x0x80485bb <main()+37>   movl   $0x12c,(%eax)                    x
       x0x80485c1 <main()+43>   lea    0x10(%esp),%eax                  x
       x0x80485c5 <main()+47>   mov    %eax,0x1c(%esp)                  x
       x0x80485c9 <main()+51>   mov    0x10(%esp),%edx                  x
       x0x80485cd <main()+55>   mov    0x14(%esp),%eax                  x
       x0x80485d1 <main()+59>   mov    %edx,0x8(%esp)                   x
       x0x80485d5 <main()+63>   mov    %eax,0x4(%esp)                   x
       x0x80485d9 <main()+67>   movl   $0x8048764,(%esp)                x
       x0x80485e0 <main()+74>   call   0x8048498 <printf@plt>           x
       x0x80485e5 <main()+79>   mov    0x1c(%esp),%eax                  x
       x0x80485e9 <main()+83>   mov    (%eax),%edx                      x
       x0x80485eb <main()+85>   mov    0x18(%esp),%eax                  x
       x0x80485ef <main()+89>   mov    (%eax),%eax    

    可以看到

     

    $0x12c存入eax指向的内存,而eax指向的内存

    (gdb) print refI
    $3 = (int &) @0xbffff604: 100
    (gdb) print $eax
    $4 = -1073744380
    (gdb) print *($eax)
    $5 = 100
    (gdb) print &i
    $6 = (int *) 0xbffff604
    

    使用计算器换算一下十六进制和十进制,i的地址604和eax的值-1073744380是一样的,eax存的是i的地址,进行赋值操作也是直接给i所在的内存赋值。
    所以,引用本质上是指针(32位也需要占用4字节空间)。

     

     

     

    至于

    和指针的操作层次不一样(指针还要再加*操作才能得到值);

    只能初始化,不能再赋值

    这都是C++和编译器的规则了!也加上他毕竟和指针的层不一样,int &refI = i;这种特定操作只有声明时候有,注定不能像指针一样用refI = j;这种形式再绑定其他变量,用*操作又等于提取内存的值。

    其他的话,想不到什么原理,目的估计就是用着方便(不用写提取符号*)和实现特定绑定(不能更改的特性,或者也可以叫防呆设定吧,安全设定,引用比指针操作起来安全,这都算是优点)。

     

    做一个char类型的例子,char &refC = c;可以看到refC实际上存了一个c的地址,而不是副本,所以这个实际需要的存储空间是一个指针的大小。

     

     

      x0x8048566 <main()>                                                      push   %ebp                                                     x
       x0x8048567 <main()+1>                                                    mov    %esp,%ebp                                                x
       x0x8048569 <main()+3>                                                    sub    $0x10,%esp                                               x
       x0x804856c <main()+6>                                                    movb   $0x63,-0x5(%ebp)                                         x
    B+ x0x8048570 <main()+10>                                                   lea    -0x5(%ebp),%eax                                          x
      >x0x8048573 <main()+13>                                                   mov    %eax,-0x4(%ebp)                                          x
       x0x8048576 <main()+16>                                                   mov    -0x4(%ebp),%eax                                          x
       x0x8048579 <main()+19>                                                   movb   $0x64,(%eax)                                             x
       x0x804857c <main()+22>                                                   mov    $0x0,%eax  

    对于引用,sizeof()不能准确反映内部情况,也许是在sizeof()调用时通过地址找到原变量c,传递的时候就和普通变量正常调用一样。

     

    实测,已经被编译器优化成这样了:

    x0x80485b3 <main()+29>                                                   movl   $0x1,0x18(%esp)

    差点忘了,sizeof是操作符,并不是函数!!!!这个操作肯定是提前约定好了!!!因为在程序运行前,还有大篇幅的初始化之类的操作,看不太懂了。

     

     

     

     

     

     

    至于给引用的赋值是直接在原变量的地址上赋值,还是把变量读出来?

    (refI = 300)这是取出地址到eax,直接从eax取出内存地址,给内存赋值的。

     

    B+>x0x80485df <main()+73>                                                   mov    0x2c(%esp),%eax                                          x
       x0x80485e3 <main()+77>                                                   movl   $0x12c,(%eax)                                            x
    

    (refC = 'D'),同上

     

     

    0x80485b4 <main()+30>                                                   mov    0x28(%esp),%eax                                          x
       x0x80485b8 <main()+34>                                                   movb   $0x64,(%eax)  

    也是,只是赋值,不加减,从内存读到eax,再从eax写回去,没什么必要!
     

    光顾着gdb调试看原理了,忘了总结一条重点了,函数参数为引用时,函数的栈不需要额外存储引用,实际上什么也不传,而是直接就用ebp和偏移去找。估计要靠编译器优化和记忆。见下图:局部变量temp是ebp负向偏移0x4,也就是在swap()当前栈;而a和b,实际上也就是i和j,靠ebp正向偏移0x8和0xc去找,也就是上一层的栈。(PS:栈是负向增长,压栈等于指针减,又因为ebp是栈底,所以ebp正向偏移就是上一层的栈空间了)

    引用传递的优势终于找到了?(20200318:汇编情况可能和平台有关,寄存器方面的微小差别仍然不确定)

    引用传递比较省栈空间,少申请就少开支,就更有效率吧?!

    至于指针,还没试,但是根据指针的特性,空间开支是免不了的,借助指针能改原变量只是一种借助地址操作的巧妙用法,更改指针本身却不会对原变量产生任何影响。指针形参和其他形参本质上没有区别,都要分配空间,都是不影响原变量的副本

     

    那么引用的多层传递呢?按理说是一样的,这样多层传递的时候节省的开支就很可观了。(要和指针对比)

    测试:

     

    #include<iostream>
    #include<stdio.h>
    void func(int &c,int &d)
    {
            c = 1000;
            d = 2000;
    }
    void swap(int &a,int &b)
    {
            func(a,b);
    }
    
    int main(){
            int i = 100;
            int j = 200;
            printf("i:%d,j:%d.\n",i,j);            
            swap(i,j);
            printf("i:%d,j:%d.\n",i,j);
    }
    


    调试

     

     

    x0x8048591 <func1(int&, int&)+6>         mov    0x8(%ebp),%eax   x
       x0x8048594 <func1(int&, int&)+9>         movl   $0x5,(%eax)      x
       x0x804859a <func1(int&, int&)+15>        mov    0xc(%ebp),%eax   x
       x0x804859d <func1(int&, int&)+18>        movl   $0x6,(%eax)      x
       x0x80485a3 <func1(int&, int&)+24>        mov    0xc(%ebp),%eax   x
       x0x80485a6 <func1(int&, int&)+27>        mov    %eax,0x4(%esp)   x
       x0x80485aa <func1(int&, int&)+31>        mov    0x8(%ebp),%eax   x
       x0x80485ad <func1(int&, int&)+34>        mov    %eax,(%esp)      x
       x0x80485b0 <func1(int&, int&)+37>        call   0x8048574 <func2(x
       x

    第一层func1(),用ebp正向偏移0x8和0xc去找地址进行操作。
    (入栈sub 0x8,比较省,没复制那么多汇编语句进来看不到。)

     

    在call func2之前,进行了两个变量地址的压栈操作,这样保证了下一个frame,也就是func2,的栈帧偏移仍然是0x8和0xc,但是这貌似还是要占用空间,并不能节省空间?或者说都免不了这一步(下边有说明,入栈传参,必须的步骤),总的来说还是要省?

       x0x8048570 <frame_dummy+32>              call   *%eax            x
       x0x8048572 <frame_dummy+34>              leave                   x
       x0x8048573 <frame_dummy+35>              ret                     x
       x0x8048574 <func2(int&, int&)>           push   %ebp             x
       x0x8048575 <func2(int&, int&)+1>         mov    %esp,%ebp        x
    B+>x0x8048577 <func2(int&, int&)+3>         mov    0x8(%ebp),%eax   x
       x0x804857a <func2(int&, int&)+6>         movl   $0x3e8,(%eax)    x
       x0x8048580 <func2(int&, int&)+12>        mov    0xc(%ebp),%eax   x
       x0x8048583 <func2(int&, int&)+15>        movl   $0x7d0,(%eax)    x
       x0x8048589 <func2(int&, int&)+21>        pop    %ebp             x
       x0x804858a <func2(int&, int&)+22>        ret       

     

     

    让func1和func2都传普通变量:

     

    #include<iostream>
    #include<stdio.h>
    void func2(int c,int d)
    {
            c = 1000;
            d = 2000;
    }
    void func1(int a,int b)
    {
            a = 5;
            b = 6;
            func2(a,b);
    }
    
    int main(){
            int i = 100;
            int j = 200;
            printf("i:%d,j:%d.\n",i,j);
            func1(i,j);
            printf("i:%d,j:%d.\n",i,j);
    }
    

     

     

     

    其实不管是传递引用还是普通参数,调用函数前的压栈操作都一样的,都是压入到esp偏移0和偏移4(本例两个int变量),这两个栈空间应该就是为了向下传参用的(如果下边不调用func2(),会不会少开辟点栈空间?)

     

     

    形参都要有,只不过传递引用是给地址,传递变量是给值。下图是形参传递数值的。


    下图是引用传递地址的(负数十进制换算一下十六进制就好了):

     

     

    另外,到最后一层,func2()的时候,esp都没有自减操作了,是不需要么(另外,哪些栈空间是存返回值,以及如何返回,还没留意。。)

     

     

     

    一个指针的例子,进行对比:

     

    #include<iostream>
    #include<stdio.h>
    int func2(int *c,int *d)
    {
            *c = 1000;
            *d = 2000;
    }
    int func1(int *a,int *b)
    {
            *a = 5;
            *b = 6;
            func2(a,b);
    }
    
    int main(){
            int i = 100;
            int j = 200;
            printf("i:%d,j:%d.\n",i,j);
            func1(&i,&j);
            printf("i:%d,j:%d.\n",i,j);
    }
    

     

     

     

     

     

    如下图,调用函数时的传参,和引用是一样的

    赋值操作也是一样的(提取地址到eax,再通过eax找到内存地址,对内存直接操作):

    mov 0xc(%ebp),%eax

    movl $0x6,(%eax)

     

    额外加一句指针赋值:

     

    int func1(int *a,int *b)
    {
            a = b;
    


    局部栈变量a(副本)发生改变。

     

     

    B+ x0x8048591 <func1(int*, int*)+6>         mov    0xc(%ebp),%eax                                                                           x
       x0x8048594 <func1(int*, int*)+9>         mov    %eax,0x8(%ebp) 

     

     

     

    可见,局部的更改都只在局部栈空间,局部的指针变量的值。而指针对内容的修改,是因为对内存进行了提取,再对内存中的原变量进行操作

    而引用,首先要和指针一样传递地址,其次,修改时等于自动带了地址提取功能,改的是原变量的内存。

     

     

    PS:仔细留意可以发现函数调用时,参数入栈顺序是反的。

    比如i和j,传给a和b,先把j入栈,再把i入栈。

    据说C/C++这样做的好处是支持可变参数列表(个数)。

     

    再加一例测试:

     

    #include<iostream>
    #include<stdio.h>
    int func2(int *c,int *d)
    {
            *c = 1000;
            *d = 2000;
    }
    int func1(int *a,int *b,...)
    {
            a = b;
            *a = 5;
            *b = 6;
    
            func2(a,b);
    }
    
    int main(){
            int i = 100;
            int j = 200;
            int k = 300;
            int l = 400;
            printf("i:%d,j:%d.\n",i,j);
            func1(&i,&j,&k,&l);
            printf("i:%d,j:%d.\n",i,j);
    }
    

     

    调试,变量的声明:

     

       x0x80485c6 <main()+9>    movl   $0x64,0x1c(%esp)                 x
    B+ x0x80485ce <main()+17>   movl   $0xc8,0x18(%esp)                 x
       x0x80485d6 <main()+25>   movl   $0x12c,0x14(%esp)                x
       x0x80485de <main()+33>   movl   $0x190,0x10(%esp) 

    调用函数时入栈顺序刚好相反:

     

     

    B+ x0x8048602 <main()+69>   lea    0x10(%esp),%eax                  x
       x0x8048606 <main()+73>   mov    %eax,0xc(%esp)                   x
      >x0x804860a <main()+77>   lea    0x14(%esp),%eax                  x
       x0x804860e <main()+81>   mov    %eax,0x8(%esp)                   x
       x0x8048612 <main()+85>   lea    0x18(%esp),%eax                  x
       x0x8048616 <main()+89>   mov    %eax,0x4(%esp)                   x
       x0x804861a <main()+93>   lea    0x1c(%esp),%eax                  x
       x0x804861e <main()+97>   mov    %eax,(%esp) 

    仔细分析,一个帧(frame)的栈结构也出来了:帧栈的前半部分(因为地址递减,所以是高地址)是本地参数,帧栈的后半部分(低地址)是保留下来,给函数传参用的。(大小随要调用函数的不同而不同。

     

    无法画线,大概示意图如下:
    $ebp

    i 0x1c(%esp)--------------------->|

    j 0x18(%esp)-------------->|      |

    k 0x14(%esp)------->|      |      |

    l 0x10(%esp)->|     |      |      |

    param4 0xc(%esp) <-|      |      |      |

    param3 0x8(%esp)<---------|      |      |

    param2 0x4(%esp)<----------------|      |

    param1 0x0(%esp)<-----------------------|

    $esp

     

     

       x0x80485bd <main()>      push   %ebp                             x
       x0x80485be <main()+1>    mov    %esp,%ebp                        x
       x0x80485c0 <main()+3>    and    $0xfffffff0,%esp                 x
       x0x80485c3 <main()+6>    sub    $0x20,%esp  

     

     

    不解的一点是,无论是两个参数还是4个参数,main压栈时都是同样的与操作和自减20。栈底都是esp偏移0x1c和0x18。但是这个栈空间只够4个参数的,五个怎么办?

    下边是六个变量的声明和传参测试(代码就不贴了):

    声明与定义:

     

    0x80485bd <main()>                      push   %ebp             x
       x0x80485be <main()+1>                    mov    %esp,%ebp        x
       x0x80485c0 <main()+3>                    and    $0xfffffff0,%esp x
       x0x80485c3 <main()+6>                    sub    $0x40,%esp       x
       x0x80485c6 <main()+9>                    movl   $0x1,0x3c(%esp)  x
       x0x80485ce <main()+17>                   movl   $0x2,0x38(%esp)  x
       x0x80485d6 <main()+25>                   movl   $0x3,0x34(%esp)  x
       x0x80485de <main()+33>                   movl   $0x4,0x30(%esp)  x
       x0x80485e6 <main()+41>                   movl   $0x5,0x2c(%esp)  x
       x0x80485ee <main()+49>                   movl   $0x6,0x28(%esp)  x

    传参与调用:

    B+>x0x8048612 <main()+85>   lea    0x28(%esp),%eax                  x
       x0x8048616 <main()+89>   mov    %eax,0x14(%esp)                  x
       x0x804861a <main()+93>   lea    0x2c(%esp),%eax                  x
       x0x804861e <main()+97>   mov    %eax,0x10(%esp)                  x
       x0x8048622 <main()+101>  lea    0x30(%esp),%eax                  x
       x0x8048626 <main()+105>  mov    %eax,0xc(%esp)                   x
       x0x804862a <main()+109>  lea    0x34(%esp),%eax                  x
       x0x804862e <main()+113>  mov    %eax,0x8(%esp)                   x
       x0x8048632 <main()+117>  lea    0x38(%esp),%eax                  x
       x0x8048636 <main()+121>  mov    %eax,0x4(%esp)                   x
       x0x804863a <main()+125>  lea    0x3c(%esp),%eax                  x
       x0x804863e <main()+129>  mov    %eax,(%esp)                      x
       x0x8048641 <main()+132>  call   0x804858b <func1(int*, int*, ...)x
       x

    总结或猜测,终于,main压栈,esp变成自减40了,估计是栈的一个取整的模式,够多少个变量和参数,main函数加0x10的栈空间。

     

    PS:此例esp自减虽然是0x20,如果传参只传两个,esp总的自减是0x10,所以单位是0x10。下边的参数也有以0x10为单位的。
    帧栈的顶格前(不是半)部分是参数,帧栈的顶格后(不是半)部分是要传递的参数,中间是预留。

     

    示意图如下:
    $ebp

    i 0x1c(%esp)--------------------->|

    j 0x18(%esp)-------------->|      |

    k 0x14(%esp)------->|      |      |

    l 0x10(%esp)->|     |      |      |

    m

    n

    .........

    ........

    param6

    param5

    param4 0xc(%esp) <-|      |      |      |

    param3 0x8(%esp)<---------|      |      |

    param2 0x4(%esp)<----------------|      |

    param1 0x0(%esp)<-----------------------|

    $esp

     

    这样就直观了,帧栈的空间是要预先分配好的,多大就是多大,假设变量一百个,但是给函数传的参数未必有一百个,但总不能就用100+2的大小啊,所以要预留出来一定大小的空间,也就是中间的一片空白。

     

    具体来说,这个大小应该是编译阶段根据实际代码已经计算出来决定了,不然怎么分配合适的大小呢。

    当然变量和参数也不是对应关系,只是本例这样用,比如声明6个变量,传2个变量,栈空间是0x30。

     

     

     

    最后,可变参数列表这多出来的参数要怎么使用呢?又没有形参。

    用argv[]之类的?是默认的还是要手动写的?

    前边的a和b也可以用argv[]吗?

    这应该是另一个基础,我给忘了。

    ================================================================================================
    ================================================================================================
    ================================================================================================
    20200318
    用windows+VS2015重新看了这个问题:debug下看到引用和指针汇编使用方法一样,release则不可以反汇编。
    但是如果跟踪变量地址的话,可以看到,引用确实不占用空间,也许对于编译器来讲,它真的就是个“别名”,编译成执行文件,其实就等于是一个替换操作,用ri的地方就等于用i1。
     
     
     
     
     
     
     
     
    所以,是不是矛盾了?
    既然引用ri不占用空间,为什么这里不是直接拿[i1]赋值?怎么像pi一样去找它取一下它存的“地址”?
    pi的值,0x0000007447cff574
    从后者取出来的值,0x00000005,到eax
    mov到[i5]
    ri的值,0x00000005
    从后者取出来的值,0x00000005,到eax
    mov到[i5]
     
    区别:ri的地址即是数值5,把5当做地址,取出来还是5.
     
     
    我自闭了?地址5的内容是5,这是trick?
     
     
    而是像指针般还要取[ri]一次地址,再找地址对应的数值???我只能解释为,这是debug,可能没优化到最佳?或者没法跟?
     
     
     
     
     
     
     
    展开全文
  • 前言: 本文只浅显地介绍了三种函数参数传递的方式及其优劣,主要目的是为了介绍引用。在另一篇博文中将会用本文中的...C++函数参数传递方式: 一、按值传递 二、按指针传递 三、按引用传递 引用的定义、特点、优势

    前言:

    本文只浅显地介绍了三种函数参数传递的方式及其优劣,主要篇幅是介绍引用及引用作为参数传递的优点。在另一篇博文中将会用本文中的主要代码反汇编为汇编代码,从编译器处理函数调用的层面更深入地探讨三种函数参数传递的区别。


    C++函数参数传递方式:

    一、按值传递
    #include <iostream>
    
    using namespace std;
    
    int Add (int a, int b)
    {
        return a+b;
    }
    int main (void)
    {
        int x = 1, y = 2;
        cout << Add(x,y) << endl;
        return 0;
    }

    像例子中主调函数直接把值(实参)传递给被调函数(形参)的形式就是按值传递,这是最为简单常见的函数参数传递方式。


    但如果被调函数要修改主调函数传过去的实参,这种方式就不奏效了:

    #include <iostream>
    
    using namespace std;
    
    void Swap (int a, int b)
    {
        int temp;
        temp = a;
        a = b;
        b = temp;
    }
    
    int main (void)
    {
        int x = 10, y = 20;
        Swap(x, y);
        cout << "x = " << x << "," << "y = " << y << endl;
        return 0;
    }



    Swap函数的作用是交换xy的值,但调用后并没有交换。

    为什么呢?我们添加一些代码看看:

    #include <iostream>
    
    using namespace std;
    
    void Swap (int a, int b)
    {
        cout << "形参地址:\n";
        cout << "a is at: " << &a << endl;
        cout << "b is at: " << &b << endl;
        int temp;
        temp = a;
        a = b;
        b = temp;
    }
    
    int main (void)
    {
        int x = 10, y = 20;
        cout << "实参地址:\n";
        cout << "x is at: " << &x << endl;
        cout << "y is at: " << &y << endl;
        Swap(x, y);
        cout << "x = " << x << "," << "y = " << y << endl;
        return 0;
    }



    从运行结果不难看出,Swap函数被调用时,会为形参分别创建新的名为ab的变量,并且将主调函数中实参xy的值分别赋给ab,这就相当于被调函数Swap为实参创建了一个副本(自然会为新变量分配内存,但这块内存与用于存储变量的普通内存不太一样)作为Swap的形参,当被调函数完成时副本即被销毁,因此对按值传递参数的方式并不会对主调函数传递过去的实参产生任何影响(Swap使用的是实参的副本,而不是原来的数据)。所以当我们需要被调函数修改实参的时候就需要别的函数参数传递方式。


    二、按指针传递

    顾名思义,按指针传递就是主调函数把指针传递给被调函数的形参,这种函数参数传递方式可以做到按值传递做不到的工作:让被调函数修改实参的值。

    #include <iostream>
    
    using namespace std;
    
    void Swap (int* a, int* b)
    {
        int temp;
        temp = *a;
        *a = *b;
        *b = temp;
    }
    
    int main (void)
    {
        int x = 10, y = 20;
        int* p1 = &x;
        int* p2 = &y;
        Swap(p1, p2);
        cout << "x = " << x << "," << "y = " << y << endl;
        return 0;
    }


    为什么按指针传递可以修改实参呢?看下一段代码:

    #include <iostream>
    
    using namespace std;
    
    void Swap (int* a, int* b)
    {
        cout << "形参地址:\n";
        cout << "a is at: " << a << endl;
        cout << "b is at: " << b << endl;
        int temp;
        temp = *a;
        *a = *b;
        *b = temp;
    }
    
    int main (void)
    {
        int x = 10, y = 20;
        int* p1 = &x;
        int* p2 = &y;
        cout << "x is at: " << &x << endl;
        cout << "y is at: " << &y << endl;
        cout << "实参地址:\n";
        cout << "p1 is at: " << p1 << endl;
        cout << "p2 is at: " << p2 << endl;
        Swap(p1, p2);
        cout << "x = " << x << "," << "y = " << y << endl;
        return 0;
    }


    从运行结果可以看到,形参ab的地址分别与实参p1p2的值相同。由于指针p1p2分别指向要交换数据的xy(即p1p2分别存放xy中存放数值的地址),因此ab也分别指向xy,因此形参通过获取到实参传递过来的目标地址可以访问xy,自然也就能修改其值了。实际上按指针传递并没有这么简单,这个在另一篇博文中会深入探讨。


    乍一看前面两种函数参数传递方式已经能够满足我们对编写代码的需求,事实上也确实如此,C语言就只有这两种函数参数传递方式。对于例子中传递基本类型的参数还看不出什么,但如果是传递复合类型的参数如结构体、类等等呢?按值传递会创建副本,对于占用内存大的参数显然不是一个好的选择,那就只剩下按指针传递了,但指针是C/C++中最为强大也最难控制的一种类型,如果在某些情况下需要用到多级指针,那么程序的可读性和鲁棒性就会大打折扣,并且对不熟悉指针的程序员来说极易造成程序安全问题(比如内存泄漏)。C++为此提供了另一种强大实用、较易理解又相对安全的类型——引用,来传递参数。


    三、按引用传递
    #include <iostream>
    
    using namespace std;
    
    void Swap (int& a, int& b)
    {
        int temp;
        cout << "a is at: " << &a << endl;
        cout << "b is at: " << &b << endl;
        temp = a;
        a = b;
        b = temp;
    }
    int main (void)
    {
        int x = 10, y = 20;
        cout << "x is at: " << &x << endl;
        cout << "y is at: " << &y << endl;
        Swap(x, y);
        cout << "交换后:x = " << x << ",y = " << y << endl;
        cout << "x is at: " << &x << endl;
        cout << "y is at: " << &y << endl;
        return 0;
    }


    从运行结果看到,实参和形参的地址都是相同的,这点与按指针传递相同,但不代表两者本质上一样。


    左值引用

    定义:

    引用是已定义的变量的别名(另一个名称)。

    特点:

    1.必须在创建时初始化
    int a = 10;
    int& b = a;

    诸如int& b = 10; int& b;都是非法声明。其中,&不是地址运算符,而是类型标识符的一部分,就像int*指的是指向int的指针一样,int&指的是指向int的引用。

    2.引用变量一旦与某一变量关联起来,就会从一而终,不会再与其它变量关联。
    #include <iostream>
    
    using namespace std;
    
    int main (void)
    {
        int a = 10, b = 20;
        int& x = a;
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "x = " << x << endl;
        cout << "a is at: " << &a << endl;
        cout << "b is at: " << &b << endl;
        cout << "x is at: " << &x << endl;
        cout << endl;
        x = b;
        cout << "执行x = b之后:" << endl;
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "x = " << x << endl;
        cout << "a is at: " << &a << endl;
        cout << "b is at: " << &b << endl;
        cout << "x is at: " << &x << endl;
        return 0;
    }


    从程序运行结果可以看到,一旦x被声明为a的引用,就会一直与a关联(引用变量的整个生命周期都是如此),试图通过x = b来将x修改为b的引用也无法成功(即使通过指针来修改也无法成功),x的地址与a的地址一直相同。简而言之,可以通过初始化声明来设置引用,但不能通过赋值来设置。

    3.不能声明诸如数组的引用

    这个特点的具体原因需要牵涉到数组与指针的关系以及计算机对数组的寻址问题,本文不深入探讨。

    总结:

    当我们写下int x = 10;这行代码时,系统将为变量x分配内存并将10存入这块内存,但本质上系统开辟内存是为了int型数据,x只不过是这块内存的名字,x本身并不占空间。而对于引用变量来说,它也只不过是变量名的一个别名,本身也不占内存。但与普通变量的区别在于引用变量具有依附性,所以在声明时就要进行初始化让其依附于一个变量,并在它的整个生命周期一直依附于这个变量,变量消亡引用也就没有了存在的意义,随之消亡。


    引用与指针:

    对指针有一定了解的人从引用的各个特点以及将引用作为参数传递的结果不难看出,引用与const指针在这方面非常相似。也就是说,int& x = a;实际上是int* const p = &a;这行代码的伪装表示。这并不代表两者完全相同,实际上在作为函数参数传递时引用的效率要比const指针更高,原因会在另一篇博文深入探讨。

    为什么引用更为安全实用:

    1.引用作为参数传递可以任意选择是否影响实参

    如果我们需要形参影响实参,按照普通引用来定义实参即可;

    如果我们不想引用修改是实参也可以,只需要声明常引用。

    例如下面的函数就是非法的,编译器会报错:

    void Swap (const int& a, const int& b)
    {
        int temp;
        temp = a;
        a = b;
        b = a;
    }

    2.临时变量、引用参数和const

    如果实参与引用参数不匹配,C++将生成临时变量。仅当参数为const引用时,C++才允许这样做。

    #include <iostream>
    
    using namespace std;
    
    
    double refcube (const double& ra)
    {
        cout << "ra is at: " << &ra << endl;
        return ra*ra*ra;
    }
    
    int main (void)
    {
        double side = 3.0;
        double *pd = &side;
        double &rd = side;
        long edge = 5L;
        double lens[4] = {2.0, 5.0, 10.0, 12.0};
        double c1 = refcube(side);
        cout << "c1 = " << c1 << endl << "side is at: " << &side << endl;
        double c2 = refcube(lens[2]);
        cout << "c2 = " << c2 << endl << "lens[2] is at: " << &lens[2] << endl;
        double c3 = refcube(rd);
        cout << "c3 = " << c3 << endl << "rd is at: " << rd << endl;
        double c4 = refcube(*pd);
        cout << "c4 = " << c4 << endl << "pd is at: " << pd << endl;
        double c5 = refcube(edge);
        cout << "c5 = " << c5 << endl << "edge is at: " << &edge << endl;
        double c6 = refcube(7.0);
        cout << "c6 = " << c6 << endl;
        double c7 = refcube(side+10.0);
        cout << "c7 = " << c7 << endl;
        return 0;
    }



    参数sidelens[2]rd*pd都是有名称的、double类型的数据对象,因此可以为其创建引用,而不需临时变量。然而,edge虽然是变量,类型却不正确,double引用不能指向long。另一方面,参数7.0side+10.0的类型都正确,但没有名称,在这些情况下,编译器都将生成一个临时匿名变量,并让ra指向它。这些临时变量只在函数调用期间存在,此后编译器就可以随意将其删除。虽然从程序中我们无法得到后两个函数调用中实参的值,但从ra的地址可以看出其存在于栈内存中,因此后两个函数中实参的值肯定与ra不同(原因在另一篇博文中会讲)。

    但如果接受引用参数的函数的意图是修改作为参数传递的变量,编译器就会禁止创建临时变量。而refcube函数的目的只是使用传递的值,不是修改它们,因此临时变量不会造成任何不利影响,反而会使函数在可处理的参数种类方面更通用。因此,如果将引用指定为constC++将在必要时生成临时变量。实际上,对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。


    3.将引用用于结构和类

    引用非常适合用于结构和类。确实,引入引用主要是为了用于这些类型的,而不是基本的内置类型。

    4.返回引用更为高效

    传统返回机制与按值传递参数类似:计算关键字return后面的表达式,并将结果返回给调用函数。从概念上说,这个值被复制到一个临时位置(只是从概念上),而调用程序将使用这个值。如果需要返回的参数是结构体,那么传统返回机制将会把整个结构复制到一个临时位置,再由调用程序使用(其实就是复制)这个复制品,但返回引用的话就会直接将结构体复制给调用程序使用,效率更高。但要注意避免返回函数终止时不再存在的数据类型的引用。



























    展开全文
  • C++参数传递机制

    千次阅读 2014-11-20 22:05:12
    C++参数传递机制  C++一共有三种传递方式:值传递(pass by value)、指针传递(pass by pointer)、引用传递(pass by reference)。 关键点:在函数中,编译器总是要为函数的每个参数制作临时副本。引用...
    C++的参数传递机制 

    C++一共有三种传递方式:值传递(pass by value)、指针传递(pass by pointer)、引用传递(pass by reference)。
    关键点:在函数中,编译器总是要为函数的每个参数制作临时副本。引用传递除外。

    一.值传递。

    值传递很简单。唯一要注意的就是当值传递的输入参数用户自定义类型时,最好用引用传递代替,并加上const关键字。因为引用传递省去了临时对象的构造和析构(见关键点)。
    数据类型为内部类型时,不必。

    例如:
    将void Func(A a) 改为void Func(const A &a);
    而void Func(int a)就没必要改成void Func(const int &a)。

    二.指针传递。
    今天之所以会花时间参阅C++的参数传递机制,就是因为对指针传递这一部分的应用出了问题。

    例如:
    在下面的getMemory函数中获得动态内存分配的指针p,并未能将获得的内存返回。

    void getMemory(char *p, int num)
    {
        p = new char[num];
    }
     
    void Test(void)
    {
        char *str = NULL;
        getMemory(str, 100);    // str 仍然为 NULL
        strcpy(str, "hello");        // 运行出错
        delete [] p;
    }

    出错的原因就是在于上面提到的关键点
    编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。
    而在上例中, 副本_p申请了新的内存,只是把 _p所指的内存地址改变了,但是p丝毫未变。所以函数getMemory并不能输出任何东西。并且,每执行一次getMemory就会泄露一块内存,因为没有用free释放内存。

    解决方法有三种:

        甲)利用全局变量。
    定义一个全局变量str,这样就不用把它作为参数来传递,也就不会有副本 _str。
    char *str = NULL;
    void getMemory(int num)
    {
        str = new char[num];
    }

        乙)用“指向指针的指针”。
    void getMemory(char **p, int num)
    {
        *p = new char[num];
    }
    void Test(void)
    {
        char *str = NULL;
        getMemory(&str, 100);  // 注意参数是 &str,而不是str
        strcpy(str, "hello");
        cout<< str << endl;
        delete [] str;
    }

        丙)返回指针变量
    char *getMemory(int num)
    {
        char *p = new char[num];
        return p;

    void Test(void)
    {
        char *str = NULL;
        str = getMemory(100);
        strcpy(str, "hello");
        cout<< str << endl;
        delete [] str;
    }

    三.引用传递。
    C++区别于C的一个新增优势。虽然用&作为标识符,但是跟指针一点关系都没有
    int &a=b表示,a和b是同一个变量。简单地说,相当于一个别名,如:Stephen Chow周星驰
    当作为参数传递时,它省去了临时对象的构造和析构。

    用法如下:
    Func(int &a)
    {
        a++;
    }
    void Test(void)
    {
        int i;
        Func(i);
    }
    展开全文
  • 1.普通传递 void f( int p){ printf("\n%x",&p); printf("\n%x",p); p=0xff; } void main() { int a=0x10; printf("\n%x",&a); printf("\n%x\n",a); f(a); printf("\n%x\n",a);...2.引用传递 void f

    1.值传递

    void f( int  p){
    	printf("\n%x",&p);
    	printf("\n%x",p);
    	p=0xff;
    }
    void main()
    {
    	int a=0x10;
    	printf("\n%x",&a);
    	printf("\n%x\n",a);
    	f(a);
    	printf("\n%x\n",a);
    }
    


    通过上例我们可以看到,int a=0x10,存放的地址为0x12ff44,值为10,当调用f(a)时,传递给p的值为10,但是p的地址为0x12fef4,当改变p=0xff,时是改变地址为0x12fef4中的内容,并没有改变0x12ff44中的内容,所以调用f(a),后a的值仍然为0x10,所以值传递无法改变变量的值。示意图如下:


    2.引用传递

    void f( int & p){
    	printf("\n%x",&p);
    	printf("\n%x",p);
    	p=0xff;
    }
    void main()
    {
    	int a=0x10;
    	printf("\n%x",&a);
    	printf("\n%x\n",a);
    	f(a);
    	printf("\n%x\n",a);
    }
    

    通过上面引用传递传递案例我们可以看到,调用f(a)时,传递给p的是a的地址,所以p和a的地址都是0X12ff44,所以p就是a,改变p当然能改变a。示意图如下:

    3.指针传递

    void f( int*p){
    	printf("\n%x",&p);
    	printf("\n%x",p);
    	printf("\n%x\n",*p);
    	*p=0xff;
    }
    void main()
    {
    	int a=0x10;
    	printf("\n%x",&a);
    	printf("\n%x\n",a);
    	f(&a);
    	printf("\n%x\n",a);
    }
    



    通过指针传递的案例我们可以看到,调用f(&a)是将a的地址0x12ff44传递给p,则*p就指向了a的内容,改变*p后,a的内容自然就改变了,示意图如下:


    4.指针的引用传递

    void f( int*&p){
    	printf("\n%x",&p);
    	printf("\n%x",p);
    	printf("\n%x\n",*p);
    	*p=0xff;
    }
    void main()
    {
    	int a=0x10;
    	printf("\n%x",&a);
    	printf("\n%x\n",a);
    	int *b=&a;
    	printf("\n%x",&b);
    	printf("\n%x",b);
    	printf("\n%x\n",*b);
    	f(b);
    	printf("\n%x\n",a);
    }
    



    为了使用指针的引用传递我们要新建一个指针b,然后将b的引用传递给p,其实p就是b的一个拷贝,*p=*b都指向a,所以改变*p的内容也就改变a的内容。示意图如下:

    我们再来看一下如果不用指针的引用传递会出现什么结果
    void f( int*p){
    	printf("\n%x",&p);
    	printf("\n%x",p);
    	printf("\n%x\n",*p);
    	*p=0xff;
    }
    void main()
    {
    	int a=0x10;
    	printf("\n%x",&a);
    	printf("\n%x\n",a);
    	int *b=&a;
    	printf("\n%x",&b);
    	printf("\n%x",b);
    	printf("\n%x\n",*b);
    	f(b);
    	printf("\n%x\n",a);
    	printf("\n%x\n",b);
    }
    


    从结果中我们可以看到调用f(b)时,传递给p的是b的内容,但是&b,和&p是不一样的,虽然*p和*b都指向a。示意图如下:


    5.错误案例


    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>
    
    void Allocate(char* p,int size){
    
    	printf("\n%x",&p);
    	printf("\n%x",p);
    
    	p=(char*)malloc(size);
    }
    void Free(char* p){
    	free(p);
    }
    void main()
    {
    	char *str=NULL;
    	printf("\n%X",&str);
    	printf("\n%X",str);
    	Allocate(str,100);
    	strcpy(str,"Hello World!");
    	printf("\n%s",str);
    	Free(str);
    	printf("\nstr=%s",str);
    	
    }
    



    当执行strcpy(str,"Hello World!"),时会报Unhandled exception in CPoint.exe:0xC0000005:Access Violation,这是因为我们参用的是指针传递,从运行结果我们可以看到str的地址为0x12ff44,当调用Allocate(str,100)时,传递给p的是str,的内容也就是0,所以p为0,但是&p并不是和&str一样的,所以在运行p=(char*)malloc(size)时,是给0x12fef0分配的100个字节,并没有给0x12ff44分配字节,所以*str还是空。所以会报错。

    5.正确案例

    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>
    
    void Allocate(char*& p,int size){
    	printf("\n%x",&p);
    	printf("\n%x",p);
    	p=(char*)malloc(size);
    }
    void Free(char* p){
    	free(p);
    }
    void main()
    {
    	char *str=NULL;
    	printf("\n%X",&str);
    	printf("\n%X",str);
    	Allocate(str,100);
    	strcpy(str,"Hello World!");
    	printf("\n%s",str);
    	Free(str);	
    }
    



    因为指针引用传递的是指针的拷贝,所以&str和&p,是地址是一样的,所以对p分配内容空间也就是对str分配空间,所以没有问题!

    展开全文
  • C++函数参数传递:按值传递和按引用传递  2011-11-29 14:03:52| 分类: C&C++ | 标签:c&c++ |字号 订阅 首先我们写出一段程序,这段程序用来介绍两种传递方式的区别。 #includeusing ...
  • C++参数传递

    2014-03-19 10:41:29
    C++和Java、C#语言在参数传递的时候,最大的不同就是在 C++ 中,除非显式通过指针或引用传递,否则所有变量都通过值传递。在 C# 中,除非显式通过具有 ref 或 out 参数修饰符的引用传递,否则类通过引用传递,而结构...
  • 使用引用参数的两个主要原因是: 1)程序员能够修改调用函数中的数据对象。 2)通过传递引用而不是整个数据对象,可以提高程序的运行速度。 参数传递的指导原则: 对于使用传递的值而不做修改的函数: 1)...
  • c++参数传递

    2014-01-08 18:11:49
    c++有两种参数传递的方法,一种是值传递,一种是引用传递,值传递就是把形参的值又复制一份然后给函数,引用传递就是把形参直接传递给函数。类似的,在函数的返回值返回的时候,return 的变量也有两种,如果是值传递...
  • 通过传递参数引用,可以允许函数访问(甚至修改)参数变量。要想按引用传递,必须将函数参数声明为引用。 函数swap3达到了交换a和b值的目的,从函数声明中可以看出swap3和swap1唯一的区别是在int后添加了一个...
  • c++引用参数

    万次阅读 多人点赞 2018-08-10 20:33:56
     C++参数传递方式: 1.值传递  调用时,将实参的值传递对应的形参,即为值传递。由于形参有自己独立的存储空间,又作为函数的局部变量使用,因此在函数中对任何形参值得修改都不会改变实参变量的值。简单的...
  • 文章目录1 C++传递、指针传递引用传递详解值传递:指针传递引用传递:2 数组作为函数的形参2.1 一维数组传递2.2 二维数组传递总结 1 C++传递、指针传递引用传递详解 值传递: 形参是实参的拷贝,改变...
  • 对于数组和结构这种大的数据,按值传递(pass by value)需要创建副本,占内存费时间,使用引用就只传地址,和按指针传递一样,但是使用起来又比指针简单安全,就是在使用原数据。 对于unique_ptr智能指针模板类,它只...
  • C++引用参数和引用返回值

    千次阅读 2015-07-14 09:46:26
    引用参数 从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的...在C++ 中,指针和引用经常用于函数的参数传递,然而,指针传
  • C++参数传递和java参数传递对比
  • c++多线程传递引用参数的问题

    千次阅读 2020-03-25 21:48:42
    在多线程之间传递参数时,不能简单地把线程创建当作函数调用,在进行子函数调用时,父函数的局部变量不会被销毁,而在创建线程时,若没有join该线程,那么局部变量可能会被销毁,致使引用或指针失效。 下面的代码是...
  • C++引用传递与指针传递区别

    千次阅读 2014-04-03 23:49:41
    C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的: 指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调...
  • c++引用&参数传递

    千次阅读 2016-03-29 20:11:22
    C++引用与指针的比较 引用是C++中的概念,初学者容易把引用和指针混淆一起。 一下程序中,n是m的一个引用(reference),m是被引用物(referent)。 int m; int &n = m; n相当于m的别名(绰号),对n的任何...
  • C/C++函数参数传递: 1. 值传递 2. 形参传递 3. 引用
  • 传递 :形参获得值之后与实参脱离关系 引用 首先,引用的定义和使用如下: #include using namespace std; int main() { int a=56; int &ai=a; cout; a=96; cout; ai=15; cout<<"
  • C++ 使用const 引用传递参数

    万次阅读 2016-01-22 15:18:37
    类似const & int 的形式是C++的常量引用,在函数参数参数列表中常使用const的引用
  • C++的函数参数传递有三种方法:值传递、引用传递、指针传递
  • 在进行参数传递时,数组引用可以帮助我们防止数组退化为指针,而这是我们在编程中很难注意到的问题。 下面来看一个实例: #include void each(int int_ref[10]) {  std::cout  for(int i=0;i  
  • c++ 参数传递机制

    千次阅读 2018-02-08 10:17:31
    C++参数传递机制&nbsp;C++一共有三种传递方式:值传递(pass by value)、指针传递(pass by pointer)、引用传递(pass by reference)。关键点:在函数中,编译器总是要为函数的每个参数制作临时副本。引用...
  • C++函数参数引用传递和值传递

    千次阅读 2014-09-13 14:20:31
    从概念上讲。指针从本质上讲就是存放变量地址 的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的...在C++ 中,指针和引用经常用于函数的参数传递,然而,指针
  • c++ 引用方式传递数组

    千次阅读 2017-08-10 14:14:00
    传递 (pass by value),指针传递... 引用传递(pass by refenrence),在内存中没有产生形参。效率大大提高!也不用处理指针的析构问题。 通过以上分析,我们设计程序时应该尽量使用引用,少利用指针。 头文件...
  • c++ vector 引用传递

    万次阅读 2019-01-19 10:55:42
    c++中vector作为参数,在调用函数中需修改vector的值时,要用到引用传递。 vector作为参数的两种引用传递的方式: 1、 void func1(vector&lt;int&gt; &amp;q){ ... } int main(){ vector&lt;...
  • C++:const + 引用传递参数与const + 值传递参数的区别 考虑如下两段代码参数传递有何差别 void Slove(const vector<int> &v){ //函数实现 } void Slove(const vector<int> v){ //函数实现 } ...
  • Java 无指针 普通类型是值传递,类类型是引用传递...C++ 存在指针 若无引用操作符,则普通类型和类类型都是值传递引用传递需要引用操作符。区别: Java的引用传递无法交换两个对象。 C++引用传递可以交换两个对象。
  • C++参数传递方式讨论

    千次阅读 2013-08-05 23:01:53
    众所周知,在C++中调用函数时有三种参数传递方式:  (1)传值调用;  (2)传址调用(传指针);  (3)引用传递;  实际上,还有一种参数传递方式,就是全局变量传递方式。这里的“全局”变量并不见得就是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 159,740
精华内容 63,896
关键字:

c++引用参数传递

c++ 订阅