值传参、指针传参、引用传参

3种传参方式:值传参、指针传参、引用传参

我们知道传参方式有3种:值传参、指针传参、引用传参。这三种方式书写方式各有不同,接下来,我们简单介绍3种传参方式实现的机制。首先看一个程序:这个程序实现的是交换2个变量的值。

#include <iostream>
//交换函数
void val_swap(int a, int b);   //值传参
void ptr_swap(int *a, int *b); //指针传参
void ref_swap(int &a, int &b); //引用传参
int main(int argc, char* argv[])
{
    int a = 10, b = 20;
    std::cout << "值传参前,a = " << a << ", b = " << b << std::endl;
    val_swap(a,b);
    std::cout << "值传参后,a = " << a << ", b = " << b << std::endl;
    std::cout << std::endl;

    std::cout << "指针传参前,a = " << a << ", b = " << b << std::endl;
    ptr_swap(&a, &b);
    std::cout << "指针传参后,a = " << a << ", b = " << b << std::endl;
    std::cout << std::endl;


    std::cout << "引用传参前,a = " << a << ", b = " << b << std::endl;
    ref_swap(a, b);
    std::cout << "引用传参后,a = " << a << ", b = " << b << std::endl;
    std::cout << std::endl;

    getchar();
    return 0;

}

void val_swap(int a, int b) {
    int temp = a;
    a = b;
    b = a;
}
void ptr_swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

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

实现结果:

  1. 值传参并没有实现交换功能。我们可以通过反汇编查看一下它的内部实现机制。汇编代码如下。
31:     int temp = p;
01301D1E  mov         eax,dword ptr [p]  
01301D21  mov         dword ptr [temp],eax  
    32:     p = q;
01301D24  mov         eax,dword ptr [q]  
01301D27  mov         dword ptr [p],eax  
    33:     q = temp;
01301D2A  mov         eax,dword ptr [p]  
01301D2D  mov         dword ptr [q],eax  

从汇编指令可以看出,函数值传参是把所传的值压栈,然后赋值给形参,例如主函数中的a传给p时,先将a压栈,然后将这个栈地址所指向的值赋值给p。这样操作的话,就是局部变量之间的交换,而不是外部变量的交换,所以值传参交换失败。

2.指针传参

36:     int temp = *p;
01301C9E  mov         eax,dword ptr [p]  
01301CA1  mov         ecx,dword ptr [eax]  
01301CA3  mov         dword ptr [temp],ecx  
    37:     *p = *q;
01301CA6  mov         eax,dword ptr [p]  
01301CA9  mov         ecx,dword ptr [q]  
01301CAC  mov         edx,dword ptr [ecx]  
01301CAE  mov         dword ptr [eax],edx  
    38:     *q = temp;
01301CB0  mov         eax,dword ptr [q]  
01301CB3  mov         ecx,dword ptr [temp]  
01301CB6  mov         dword ptr [eax],ecx 

指针传参是将该实参的地址传给形参,在调用的过程中,通过地址去寻找相应值进行交换,所以指针传参交换成功。
3. 引用传参

42:     int temp = p;
01301CDE  mov         eax,dword ptr [p]  
01301CE1  mov         ecx,dword ptr [eax]  
01301CE3  mov         dword ptr [temp],ecx  
    43:     p = q;
01301CE6  mov         eax,dword ptr [p]  
01301CE9  mov         ecx,dword ptr [q]  
01301CEC  mov         edx,dword ptr [ecx]  
01301CEE  mov         dword ptr [eax],edx  
    44:     q = temp;
01301CF0  mov         eax,dword ptr [q]  
01301CF3  mov         ecx,dword ptr [temp]  
01301CF6  mov         dword ptr [eax],ecx  

引用传参与指针传参实现的机制完全相同,这涉及到引用的本质是指针常量。但是引用在传参方面有更简洁,不会出现书写的错误。

常量引用传参

由上面我们可以得知,普通的指针传参和引用传参的实现机制是完全相同的,但是引用传参书写更加简洁。

引用有一个特殊的传参方式:常量引用传参,该方式用于在函数体内部不想改变形参的值。
比如:
Void func(const int &a);
这样传参的方式,函数体内部不能够改变变量a的值,而且这样写的程序更加规范,如果该函数体是这样写的:
Void func(int &a);
那么,如果我有一个const int a的值要传给这个函数将会报错。

传值与传引用的效率对比

请见博客:http://www.cnblogs.com/QG-whz/p/5129173.html

总结:

  1. 传值调用(Call by Value):函数(方法)在调用参数时,不是对原参数进行操作,而是创建参数的拷贝并对其进行操作,即只是利用它来进行其他计算而不改变其值。
  2. 引用是独占的,即一旦创建了引用并初始化为某特定对象,它将总是引用此对象,给引用赋值并不会使它“指向”另外的对象,只是改变了对象的值。
  3. 传地址:是传值的一种特殊方式,只是它传递的是地址,不是普通的如int 。那么传地址以后,实参 和行参都指向同一个对象。

  4. 以下是必须使用引用传参的情况:
    1:当大型的类对象必须作为参数传递时对实际的应用程序而言分配对象并拷贝到栈中的时间和空间开销往往过大.
    2:当实参的值必须被修改时,例如在函数swap()中用户想改变实参的值但是在按值传递的情况下无法做到.

  5. 使用常量引用的情况: 在函数体内部不想改变形参的值
  6. 对内置类型来说,通常传值更高效。对用于自定义类型来所,传值要经历构造与析构过程,一般比较耗时
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值