c++关于函数的参数传递全部知识点详解

c++中每次调用函数时,都会重新创建该函数的所有形参,此时传递的实参将会初始化对应的形参。关于函数参数传递这方面的知识点多而杂,下面我将详细讲解在参数传递时需要注意的关键点。

1、非引用形参:非引用形参初始化时只是复制了实参,所以在函数调用的只是实参的局部副本,所以函数中无论对副本做什么修改,都不会影响实参。代码演示:
int gcd(int v1, int v2){
while (v2){
int temp = v2;
v2 = v1%v2;
v1 = temp;
}
return v1;
}//while循环体中虽然修改了v1和v2的值,但是这些变化只是仅限于局部参数,不会对gcd函数使用的实参的值有任何影响。

2、指针形参:由于指针只是变量的一个地址,所以指针形参和上面有很大的区别。当给使用指针形参的函数传递一个实参指针时,初始化时只是复制了实参指针。所以实参指针的值是不会因为函数二发生改变。但是区别就是实参指针和副本指针都是指向同一个对象,所以通过函数的副本指针是可以修改实参指针所指向的对象的值。代码演示:
#include < iostream >
using namespace std;
void reset(int *ip){
*ip = 0;
ip = 0;
}
int main(){
int i = 42;
int *p = &i;
cout << “i:” << *p << endl;
reset(p);
cout << “i:” << *p << endl;
system(“pause”);
return 0;
};//这段代码演示的便是使用指针作为形参,在函数中便可以通过副本指针修改实参指针所指向的对象的值。输出结果:(i:42 i:0)

3、非引用非const类型形参:当函数使用非引用非const类型做形参时,则实参既可以是const类型的形参同时也可以是非const类型的形参,这种现象源于const对象的标准初始化规则。

4、非引用const类型形参:当函数使用非引用const类型做形参时;实参也是既可以是const类型的形参也可以是非const类型的形参,原因和第三条相同。

&&注意: 当遇到下面的情况时,复制参数将具有很大的局限性:
a、当需要在函数中修改实参的值时;
b、当需要以大型对象作为实参传递时,因为将要付出很大的空间作为代价;
c、当没有办法实现对象的复制时(比如数组不支持赋值);
解决办法:将形参定义成引用或者是指针类型,下面将详细讲解这种解决方式。

5、引用形参:利用引用形参可以通过函数改变实参的值。原因是引用参数是直接绑定到所绑定的对象上面的,它相当于对象的一个别名,所以并不是复制,便可以通过别名来修改对象的值。代码演示:
#include < iostream >
using namespace std;
int main(){
int a = 10, b = 20;
cout << “a=” << a << “;b=” << b << endl;
swap(a, b);
cout << “a=” << a << “;b=” << b << endl;
system(“pause”);
return 0;
};
void swap(int v1, int v2){
int temp = v2;
v2 = v1;
v1 = temp;
}/这个方法便是使用了非引用形参,所以并不能通过这个函数调换两个数据的值。
void swap(int &v1, int &v2){
int temp = v2;
v2 = v1;
v1 = temp;
}//这个方法使用了引用形参,调用此函数最终输出的结果将是v1=20;v2=10

6、引用形参的应用实例:通过引用形参可以返回额外的信息。之所以这么说的原因是因为函数只能够返回单个值,所以当需要不止一个内容需要返回时,这时在函数的形参中添加一个引用形参便可以解决这个问题。代码演示:
#include< iostream >
#include< vector >
using namespace std;

vector::iterator find_val(
vector::iterator beg,
vector::iterator end,
int value, vector::size_type &occurs){
vector::iterator res_iter = end;
occurs = 0;
for (; beg != end; beg++){
if (*beg == value){
if (res_iter == end)
res_iter = beg;
++occurs;
}
}
return res_iter;
}

int main(){
vector ivec;
int num = 0;
for (int i = 0; i < 10;i++){
ivec.push_back(num++);
}
for (vector::iterator iter = ivec.begin(); iter!=ivec.end(); ++iter){
cout << *iter << ” ” << endl;
}
system(“pause”);
vector::iterator prebeg= ivec.begin(), preend = ivec.end();
int a = 3;
vector::size_type preoccurs=0;
find_val(prebeg, preend, a, preoccurs);
cout << “occurs=” << preoccurs << endl;
system(“pause”);
}//这段程序便演示了对于函数在只有一个返回值的情况下如何使用引用形参传递出我们需要的数据。

7、非const引用形参:在c++当中一般情况下不常使用非const引用形参,因为它会给程序的编译带来很大的麻烦,下面将详细介绍非const引用形参。非const引用形参的局限性在于传递给它的实参只能够是非const对象,其他的都不行。下面用代码演示经常容易出现的错误:
int incr(int &val){
return ++val;
}
int main(){
short v1 = 0;
const int v2 = 42;
int v3 = incr(v1);//错误,不能够用普通的非const类型数据
v3 = incr(v2);//错误,不能够使用const类型数据
v3 = incr(0);//错误,不能够使用字面值作为实参
v3 = incr(v1 + v2);//错误
}//显然,使用非const引用类型是不够灵活的,在编程序时一般都使用const引用形参,下面将做详细介绍

8、const引用形参:对于const引用在使用的时候相对于非const引用形参限制条件要小很多。首先对于const引用形参可以传递const实参和非const实参,并且对于字面值都是可以作为实参传递的。另外const引用形参的一个作用就是避免复制,对于大部分类类型和大型数组,确实可以用复制机制进行参数的传递,但是那样他的效率太低了,所以对于不需要改变变量值的情况下,均采用const引用形参,这样能够避免复制实参。代码演示:

9、传递指向指针的引用:前面我们使用引用形参利函数实现了两个实参的交换,现在我将介绍另一个实现两个实参数值交换的应用。就是传递指向指针的引用,实现两个实参指针的交换,这样变同样可以实现两个实参的交换,代码演示:
#include< iostream >
using namespace std;
void ptrswap(int &v1,int &v2){
int *temp = v2;
v2 = v1;
v1 = temp;
}
int main(){
int i = 10, j = 20;
int *pi = &i;
int *pj = &j;
cout << “Before ptrswap():\t*pi:” << *pi << “;t*pj:” << *pj << endl;
ptrswap(pi, pj);
cout << “After ptrswap():\t*pi:” << *pi << “;t*pj:” << *pj << endl;
system(“pause”);
return 0;
};//这段代码便利用指针引用形参实现了两个实参数值的传递。

10、vector和其他类型的形参:在传递vector和其他类型的容器时,通过传递指向容器的需要处理的元素的迭代器来传递容器。代码演示:
#include< iostream >
#include< vector >
using namespace std;
void print(vector::iterator beg,
vector::iterator end){
while (beg != end){
cout << *beg++;
if (beg != end)
cout << ” ” << endl;
}
}
int main(){
vector ivec;
int num = 0;
for (int i = 0; i < 10; i++)
ivec.push_back(num++);
vector::iterator prebeg = ivec.begin();
vector::iterator preend = ivec.end();
print(prebeg, preend);
system(“pause”);
return 0;
};//这段代码演示了一个vector容器数据的传递。

11、数组形参:数组有两个特殊性质:第一数组不支持复制操作;第二在使用数组名字的时候,数组名会自动转化成数组类型的指针。因此在处理数组作为实参的函数时,通常使用数组的指针来处理数组。下面将介绍下面两种情况下数组做参数的实例:
a、一般情况下我们使用三种方式指定数组形参:
void printValues(int*){};
void printValues(int[]){};
void printValues(int[10]){};
在这种情况下一般不定义形参的长度,不然可能会导致误解,比如你在形参中定义了一个长度为10 的数组但是在传递的时候却传递了一个长度不是10的数组,这样在变异的时候不会出现错误,但是在运行的时候便会失败。之所以编译时不会出错的原因是:当编译器检查数组形参关联的实参时,只会检查实参是不是指针,指针的类型和数组元素的类型是不是匹配,而不会检查数组的长度。这一点要注意。

b、通过引用传递数组:数组形参也可以声明成数组的引用。如果形参是数组的引用,则编译器将不会将数组实参转化成指针,而是传递数组引用本身。在这种情况下与上面不同的一点便是数组大小将成为形参和实参的一部分。代码演示:
void printValues(int (&arr)[10]){};
int main(){
int j[2] = { 0, 1 };
int k[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
printValues(j);//这个写法便是错的
printValues(k);//这个参数的传递是正确的
};//在这种情况下编译器便会检测实参和形参数组的大小是否相等
注意:多维数组的传递也是通过引用传递数组的

12、传递给参数数组的处理:非引用数组形参的类型检测只是确保了实参是和数组元素具有同类型的指针,所以并不会检测数组的大小。下面将介绍防止数组越界的方法:
a、通过向函数传递数组的第一个和最后一个元素下一个未知的指针,代码演示:
void printValues(const int *beg, const int *end){
while (beg != end){
cout << *beg++ << endl;
}
}
int main(){
int j[2] = { 0, 1 };
printValues(j, j + 2);
return 0;
};

b、向函数传递第二个形参来表示数组的大小,代码演示:
void printValues(const int ia[],size_t size){
for (size_t i = 0; i != size; i++){
cout << ia[i] << endl;
}
}
int main(){
int j[] = { 0, 1 };
printValues(j, sizeof(j)/sizeof(*j));
return 0;
};

猜你喜欢

转载自blog.csdn.net/feilong_csdn/article/details/49127587