(C/C++)函数参数传递和返回机制

来源:互联网 发布:淘宝卖家怎么防止被骗 编辑:程序博客网 时间:2024/04/28 15:23

引:都知道函数的参数传递包括值传递、指针传递和引用传递,函数的返回内容可以是返回值、返回指针、或者返回引用。引用这一概念在C++里面提出的,了解函数的传递以及返回机制是十分重要的,这里主要用C++来描述。
一:变参函数的调用机制:
对于C语言,它的调用规则遵循_cdedl调用规则。
在_cdedl规则中:
1.参数从右到左依次入栈
2.调用者负责清理堆栈
3.参数的数量类型不会导致编译阶段的错误
二:函数的返回机制“副本”
- 不管是参数传递还是函数返回,了解“副本”这概念是非常重要的(切记!)。
- 描述:将指针变量p作为参数传递给另一个函数的时,另一个函数会临时建立一个临时 “副本”指针变量pp来接受这个指针变量所指向变量的地址值。(意思就是会复制一个相同的参数,子函数的操作实际上是对这个复制参数的操作与主函数无关)这就是为什么采用值传递时,主函数的值不会发生变化,因为被修改过的“副本”不会传入主函数,但是对于指针传递来说,情况就比较多,这里也主要对指针传递的讨论。
首先来看一个例子:

#include<iostream>//例1using namespace std;char *Change(char *p){    p += 3;//指针加3操作    return p;}int main(){    char a[] = "abcd";    char *p = a;    Change(p);//将a的地址传给子函数    cout << *p << endl;}

输出结果:a
可能你会发现,这是传址操作,把a的地址传给子函数加3,通过对p的改变来移动主函数p的指针,结果为什么不是d。
这就是我想说的副本,调用Change函数时,同时创建一个副本P(a的地址),在Change函数中对p的任何操作实际上是在对这个副本的操作,当Change函数执行完时,函数内部声明的局部变量会被释放,由内存管理器收回,所以这个副本也一同被释放,当返回main函数时,p还是指向数组a的首地址(a[0]、’a’),所以输出还是a。那么怎么才能改变p的指向呢?那就是利用二级指针(指针的指针),把main函数中p的地址传过去,Change函数通过p的地址来改变p的值(p的指向)。

#include<iostream>//例2using namespace std;char *Change(char **p){    *p += 3;//指针加3操作    return *p;}int main(){    char a[] = "abcd";    char *p = a;    Change(&p);//将p的地址传给子函数    cout << *p << endl;}

输出结果:d
对于许多初学者,可能在接触二级指针的时候,理解倒是能理解,但是不会熟练运用,难免不知道,子函数中 * p、* * p、&p、* &p等到底表示什么,这里关于函数传递机制和副本的结合,用一种形象化的方式来描述:
如果把参数传递的调用机制看成一种级别的方式,那么应该是这样的级别(这样看待只为好理解,实际没有这种说法):
main函数中有:
int a=5;
int *p=&a;
Change(&p);
那么级别为:函数调用机制限制形象描述
这里解释下,1、2、3表示级别按从大到小方式排列,同级之间等价。
如果把例子中子函数加进去:
有这样的定义 int * Change(char * * pp) //pp是个二级指针,指向p
那么级别为:函数调用机制限制形象描述
说了这么多总得来就一句话:传递过程中不能改变自己本身(除函数的return返回机制外),但可以透过这个值来改变之后的值,这就是为什么我要例举这个“级别”的原因。
在二级指针调用中,如图:如果传递的是3层的参数(如p),那么就不能改变3层的值(如例1),但可以透过3层改变4层的值;当然,如果传递的是2层的参数(如&p),按照前面的话就不能改变自己本身(这个也改变不了,P的地址是指针常量),但可以透过2层来改变之后的值(如例2),也就是3,4层。
(注:上图代表一种级别以及等价的关系,便于理解与使用,实际操作注意区分,例如&P、&a代表指针常量无法更改,画出来只是为了便于理解让你知道“ * PP”实际上就表示 “&a”)
三:函数的返回时的“副本”
当函数执行完时,函数内部声明的局部变量会自动消亡,而对应的内存被释放,由内存管理区收回。返回值被放到(相当于复制)到指定位置(可以是CPU寄存器、可以是某个内存单元),然后主函数会通过这个位置取得返回值。那么这个位置就是函数返回时为返回值创建的“副本”,这就是为什么可以用“return 局部变量”可以来返回一个值的原因。
所以例子中的代码也可以写成:

#include<iostream>//例3using namespace std;char * Change(char *p){    p += 3;//指针加3操作    return p;}int main(){    char a[]="abcd";    char *p = a;    p=Change(p);/*通过return返回,将改变过p位置的地址创立一个副本地址并赋值给主函数的p,这就实现了通过return改变自己本身*/    cout << *p << endl;}

输出结果:d

本文章灵感源于:http://blog.csdn.net/a735311619/article/details/54237677,谢谢这位笔友的讲解。

1 0
原创粉丝点击