【C++】函数传值、传指针以及传引用的不同之处、生存期以及存储位置

1、函数参数传值

void swap(int  a, int  b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
    printf("a:%d\n", a);// 3
    printf("b:%d\n", b);// 4
}

int main()
{
    int m_age = 16;
    int y_age = 5;

    printf("m_age:%d\n", m_age);// 1
    printf("y_age :%d\n", y_age );// 2
    
    swap(m_age, y_age);
    printf("m_age:%d\n", m_age);// 5
    printf("y_age :%d\n", y_age );// 6
}

        这是一个简单的传值的函数,目的是交换 m_age 和 y_age 的值。我们将每行打印都标记上序号。

1.首先是序号1 、2的打印

        这个打印毋庸置疑,m_age的值是16,y_age的值是5 。当程序从main( )开始运行到

    int m_age = 16;
    int y_age = 5;

的时候。内存为变量m_age在栈区上分配一个4字节大小的内存块,这个内存块的首地址我们假设为0x0001;然后也为y_age在栈区上分配一个4字节大小的内存块,这个内存块的首地址就为0x0005.

        那么大家可以看到,局部变量的内存分配在栈区上。其作用域只有当前函数体内有效(main函数也跟普通函数一样,只不过是被底层的汇编写作了程序的入口函数)。

        其次,栈区分配的内存竟然是连续分配的?是的,其实大家 printf("%p", &m_age) 打印他们的地址就可以发现,程序为局部变量分配内存的时候竟然是按部就班的分配。为啥会这样呢?首先大家要明白我们这里的内存并不是真实存在的,是虚拟内存,而不是真实的物理内存(磁盘)。当我们编译一个程序的时候,物理内存就会划分一块内存将其虚拟化供当前程序使用。使用完(整个程序结束)之后这块虚拟内存就会被释放(具体的内容可以自行去搜关于内存的解说)。

2.序号3、4的打印

        序号3 、4的打印是swap()函数内执行的。大家需要清楚 函数参数和函数内部的变量一样都是局部变量,保存在栈中,仅当前函数体内有效。(注意这个函数内的变量是一般的声明变量哈,就如我们例子代码一样,如果被static,const等修饰的变量就会改变其生存期)。

        那么函数参数(我们称之为形参,如本例中的int a, int b)仅函数体内有效的话,很明显序号3 、4的打印如我们预期的那样,是交换了彼此的值的。m_age == 5 、 y_age == 16。

3.序号5 、6的打印(重点)

        首先,我们称在函数外的变量供函数使用的参数为实参(如m_age 和 y_age)。将实参m_age,y_age的值传入函数swap()之后,在main()打印出来会发现m_age 和 y_age的值并没有发生任何变化

int main()
{
    int m_age = 16;
    int y_age = 5;
    
    printf("m_age:%d\n", m_age);
    printf("y_age:%d\n", y_age);

    swap(m_age, y_age);
    printf("m_age:%d\n", m_age);
    printf("y_age:%d\n", y_age);
}

输出结果:

为什么会造成这样的结果呢?实际上当 实参传入函数后,会将实参的大小和值拷贝出来存储在栈中,然后再赋值给形参a, b。我们将拷贝出来的临时变量成为副本

        可以看出,副本也是局部变量,存储在栈区,拥有自己的地址和大小,将副本赋值给形参a, b之后,操作形参a,b实际上是操作副本,与实参本身无关。所以才会造成这种现象。那么如何验证这个结论了,只需要将函数内部的形参a,b的地址打印出来即可。会发现形参地址和实参地址是不一样的。

2、函数参数传指针

        经过上面的解析。如果我们就需要改变实参本身的值怎么办呢?有两种方法,传指针传引用

void pswap(int * a, int * b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int m_age = 16;
    int y_age = 5;
    
    int * m = &m_age;
    int * y = &y_age;

    printf("m:%d\n", *m);
    printf("y:%d\n", *y);

    pswap(m, y);
    printf("m:%d\n", *m);//1
    printf("y:%d\n", *y);//2
}

         该程序的输出结果为:

可以看到,该程序实现了两个实参之间值的交换。为什么传指针就可以实现呢?首先我们要明白内存(数字化就是我们地址)是不变的,内存所存储的值是可以变的。如果我们想更改某个变量,我们找到这个变量不是因为m_age 和 y_age 这些符号(虽然程序让你看起来你是用m_age这个符号来找到这个变量的),而是通过找到这个变量所在的地址。所以我们想更改某个变量,通过它的地址去寻找真实的它。

        在本程序中。定义了两个局部变量 m_age 和 y_age ,我们需要操作这两个的值,那么首先需要找到它们的地址,而 & (取址符)就可以做到。我们取他们的地址 &m_age, &y_age将其赋值给能存储地址的变量类型(就是我们的指针,指针是一个值为内存地址的变量类型)。然后再将这个变量作为形参传入到函数中。其内在操作为:

        当程序运行到swap()函数时,为其形参分配内存(形参是个指针),并指向传入的实参的地址,这样,操作形参指针的值实际上就是操作实参指针的值。

        但是大家会发现,使用传值和传指针都会为形参分配内存,如果其内存小可能对程序运行的效率没有什么影响,但是一旦程序复杂,传入参数有多个值或者指针时,所需要的内存消耗还是很大的。

3、函数参数传引用(C++)

void rswap(int & a, int & b)
{
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main()
{
    int m_age = 16;
    int y_age = 5;
    
    int & m = m_age;
    int & y = y_age;

    printf("m:%d\n", m);
    printf("y:%d\n", y);

    rswap(m, y);
    printf("m:%d\n", m);
    printf("y:%d\n", y);
}

        以上程序的输出结果是:

         大家可以看出,传引用与传指针一样,都可以实现实参的值的改变。那这两种方式有什么区别呢?

        传引用是c++引入的一种特别的方式。其含义是给变量取别名,用&符号表示这个变量是个引用变量。其含义有点类似于C语言关键字typedef。那么既然取别名,说明只是用了另一个变量名来表示它,那么这个变量名的内存和地址很显然就是指向了原来的变量的地址

        int & m = m_age.定义一个引用变量m,将m_age赋值给这个引用变量m,那么此时,m就表示m_age, rswap(m, y)等价于rswap(m_age, y_age)

        大家可以发现,声明引用后,引用变量和原变量是完全等价的,但是真正使用时,函数原型的参数是可以直接使用原变量的,这样就会方便大家使用引用。

        简而言之,声明引用后,虽然引用变量和原变量完全等价,但是引用变量的行为就是在操作地址。

已知:int * m = &m_age; int & n = m_age
可得:*m = m_age;//*ms是解引用,表示指针m指向的整型内存的值
      n = m_age;//n是引用变量,引用变量和原变量完全等价
      *m = n;
       
      m = &m_age;//m是指针,其值必须是个地址
      &n = &m_age;//&n是取n的地址,这里不是表示引用,取n的地址就是取m_age的地址
      m = &n; 

        综上,C++引入了 引用 时,是借鉴了指针的行为的,是对指针的一种模仿。但是又和指针不同。它有着它自己的优势。

        优势一:引用既然是取别名,那么在使用引用变量传参时,实际上是传它本身,是没有额外的开销的。但指针有,指针的开销是在于传入指针时,需要开辟形参指针的内存。所以在形参是结构体数据或类的时候,用引用最好。

        优势二:传参传引用时,多于const 相结合。表示该形参变量就是实参变量,没有额外开销的同时保证其在函数内不允许被改变。这样的好处是在写代码时,不会因误用改变了原本不需要被改变的变量,减少了程序员检查代码的时间

        优势三:当形参是类的时候,必须(最好)使用引用;是结构体的时候,可以选择使用引用或者指针;

        综上,如果你是用C语言编写代码,那么毋庸置疑肯定是用传指针的方式。如果你是从事C++的开发工作,那么传引用无疑是比指针要更好的。

        那么传引用那么好,就没有什么缺点吗?

        有。引用最大的缺点就是一定要保证实参和形参的数据类型要一样,否则c++会自动转换,这样就会造成数据精度的缺失。

        缺点二:引用必须声明即初始化。如 int & m = m_age;不可以 int & m; m = m_age。

        缺点三:当形参是数组时,不可以使用引用。

以上就是对函数参数传值,传指针,传引用的一些区别、内存和生存期等问题的叙述。如果有错误的地方,欢迎大家到评论区留言。随着技术的提高,会不断提高文章的质量。瑞思拜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值