函数参数传递

来源:互联网 发布:人工智能 教育风口 编辑:程序博客网 时间:2024/06/08 16:03

每次调用函数时,都会重建它的形参,并且传入的实参会对形参进行初始化。
一般来说,形参的类型决定了形参和实参的交互方式。如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。由此我们会说,函数传参包括两种:引用传递和值传递。

值传递

值形参

初始化一个变量时,初始值被拷贝给变量,该变量的变化并不影响初始值的变化。如:

int i =1024;  //定义i为1024int j=i;  //定义j为1024j=2048; //修改j为2048,但是i的值依然是1024

在函数中定义

//交换函数,实参将i的值拷贝给a,j的值拷贝给b,所以i,j的拷贝值a,b 可以实现交换,但i,j本身并不交换void  swap(int a,int b){    int temp=0;      temp=a;    a=b;    b=temp;}int main(){    int i=1024;    int j=2014;    swap(i,j);    //不能实现交换,值传递不改变a,b 的值    return 0;}

指针形参

当执行指针拷贝操作时,拷贝的是指针的值。拷贝后,两个指针是不同的指针。因为指针使我们可以间接地访问它所指的对象,所以通过指针可以修改它所指对象的值。如:

int n=0,i=42;int *p=&n,*q=&i;  //p指向n,q指向i*p=42;//n 的值发生变化;p没有变化p=q;//p现在指向i;但是i和n的值都不变
void resetint *ip){    *ip=0;  //改变ip所指对象的值    ip=0;只改变ip的局部指向,实参并未改变}

所以传地址也是一种值拷贝。

引用传递

通过对复合类型之引用的学习,我们知道对于引用的操作实际上是作用在引用所引的对象上。

int n=0,i=42;int &r=n;  //r绑定n(即r是n的另一个名字)r=42;  //r的值为42r=i; //r的值和i的值相同i=r;//i的值和n相同

使用引用有一个好处就是避免传参时的拷贝所以当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。

const 形参和实参

有时候在传参过程中,为了保证参数的值不发生变化,需要加const 关键字进行限定。同样,const 关键字可以加在基本类型上,复合类型上,同时也可以加在自定义类型上面。然而,针对各种不同的类型,const 所表现出来的行为也不同。
在《C++Primer》中,有看到关于const 的理解,其中以顶层const 和底层const 来讨论,感觉自己难以理解。所以自己就总结了一些基础知识。

基本类型

在函数传参中,基本类型使用值传递,则涉及到的就是值的拷贝,即就是基本类型的对象赋值操作。
如果利用一个对象去初始化另外一个对象,则他们是不是const都没有关系。

int i=42;const int ci=i;  //正确,i的值被拷贝给了ciint j=ci;   //正确,ci的值被拷贝给了j

ci 的常量特征仅仅在执行改变ci 的操作才会发挥作用。

复合类型(指针和引用)

指针:
一般来说,指针的类型和它所指向的对象需要严格匹配;但是在使用const 的过程中,可以令一个指向常量的指针指向一个非常量对象,具体示例如下:

const double *ptr=nullptr;double dval=3.14;ptr=&dval;//合法,但是不能通过ptr修改dval的值,但是dval的值还是可变的。

引用:
(1)不能将一个常量引用赋值给非常量引用,代码如下:

const int i=3;  //常量对象const int &r1=i;  //引用合法int &r2=i;  //错误,i是常量引用,不能赋值给r2非常量引用

(2)在初始化常量引用时允许用任意表达式作为初始值,只要该表达式能转化成引用的类型即可。尤其是,允许一个常量引用绑定非常量的对象,字面值,甚至是一个表达式。

int i=42;const int &r1=i;  //一个常量引用绑定非常量的对象const int &r2=42; //一个常量引用绑定字面值const int &r3=r1*2; //一个常量引用绑定表达式int &r4=r1*2;  //r4是一个普通的非常量引用,两者类型不一致

所以针对const引用总结如下:允许一个常量引用绑定非常量对象;不允许一个非常量引用绑定常量对象;常量引用可以绑定多种类型。

数组传参

非const 数组

虽然在C++语言中,数组也是一种基本类型,但是由于其传参的特殊性,故将其取出来单独分析。
我们都知道,数组有两个性质:
(1)不允许拷贝数组;
(2)使用数组会将其转换成指针。
所以在函数传参的过程中不能简单的理解为数组值传递的方式进行。相反,由于数组会被转换成指针,所以当我们传递数组参数时,实际上传递的是指向数组首元素的指针。
和其他使用数组的代码一样,以数组作为形参的函数也必须确保使用数组时不会越界
由于数组时以指针的形式传递给函数的,所以一开始子函数并不知道数组的尺寸大小。因此,调用者应该为此提供一些额外的信息。通常有三种常用的方法:
(1)使用标记确定数组的长度
数组中用一个特殊标记来表明数组的结束,比如C风格中的字符串
(2)使用标准库规范
在子函数中,传递指向数组首元素和尾后元素的指针(需要实践)
(3)直接传递一个表示数组大小的形参(比较常用)
通常在使用数组作为形参时,会传递一个size 表示数组的大小

const 和数组形参

一维数组

当子函数不需要对数组元素进行写操作的时候,数组形参可以指向const的指针。只有当函数确实要改变元素值的时候,才把形参定义成指向非常量的指针。如

//形参是数组的引用,维度是类型的一部分void print(int (&arr)[10])  {    for(auto elem:arr)    {        cout<<elem<<endl;    }}
多维数组

多维数组其实就是数组的数组。
传递多维数组的过程中,首选需要将数组转换成指针形式,再将指针形式转换成引用,传递参数即可。

int *matrix[10]   //10个指针构成的数组int (*matrix)[10]  //指向含有10个整数的数组的指针
原创粉丝点击