每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。形参初始化机理与一般变量的初始化一样。
c++中传递参数方式有值传递和引用传递。由形参的类型决定,如果形参时引用类型,则其是引用传递。形参会绑定到对应实参上。即引用形参是它对应实参的别名。否则是值传递,将实参的值拷贝后赋给形参。此时形参和实参是两个相对独立的对象。
对于值传参,需要特别注意的是如果传的是指针形参:
指针的行为和其他非引用类型一样。当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后,两个指针是不同的指针(但在对形参改变之前其中的值是一样的)。因为指针使我们可以间接的访问它所指的对象,所以通过指针可以修改它所指对象的值:
1 #include <iostream> 2 using namespace std; 3 4 void gel(int *cnt){//cnt中存的是x的地址 5 *cnt += 1;//改变x对应地址里的值,即x的值被改变了 6 } 7 8 int main(void){ 9 int x = 1; 10 gel(&x); 11 cout << x << endl;//输出2 12 return 0; 13 }
如果先改变了 cnt 的值使其不再指向x,则 x 的值保持不变
1 #include <iostream> 2 using namespace std; 3 4 void gel(int *cnt){ 5 int gg = 1; 6 cnt = ≫//cnt所指的对象不再是x,变成了gg 7 cout << "the dress of cnt: " << cnt << endl; 8 *cnt += 1;//此时修改的是gg对应地址里面对象的值 9 } 10 11 int main(void){ 12 int x = 1; 13 gel(&x); 14 cout << "the dress of x: " << (&x) << endl; 15 cout << "the value of x: " << " " << x << endl;//此时x的值仍然是1 16 return 0; 17 }
如果形参类型是数组的话,在传参过程中数组会被转化成指针,因此对形参数组中的元素作修改的话实参数组的元素也会对应发生改变:
1 #include <iostream> 2 using namespace std; 3 4 void gel(int b[]){ 5 b[0] += 1; 6 } 7 8 // void gel(int *b){ 9 // *b += 1; 10 // } 11 12 //这两个gel函数是完全等价的 13 14 int main(void){ 15 int a[] = {1, 2, 3}; 16 gel(a); 17 for(auto indx : a){ 18 cout << indx << " "; 19 } 20 cout << endl; 21 //输出2 2 3 22 return 0; 23 }
同理,如果先让 b 指向其他对象的话,a 数组的元素也不会被修改:
1 #include <iostream> 2 using namespace std; 3 4 void gel(int b[]){ 5 int c[] = {1, 2, 3}; 6 b = c; 7 b[0] += 1; 8 } 9 10 int main(void){ 11 int a[] = {1, 2, 3}; 12 gel(a); 13 for(auto indx : a){ 14 cout << indx << " "; 15 } 16 cout << endl; 17 //输出1 2 3 18 return 0; 19 }
c++primer 5th:熟悉 c 的程序员常常使用指针类型的形参访问函数外部的对象。在 c++ 中,建议使用引用类型的形参代替指针。
引用传参:
1 #include <iostream> 2 using namespace std; 3 4 void gel(int &x){ 5 x += 1;//x绑定了对象cnt,修改x即修改cnt 6 } 7 8 int main(void){ 9 int cnt = 1; 10 gel(cnt); 11 cout << cnt << endl;//输出2 12 return 0; 13 }
使用引用避免拷贝:
拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括 IO 类型在内)不支持拷贝操作。当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。
如:写一个函数比较亮个 string 对象的长度。因为 string 对象可能会非常长,所以应该尽量避免拷贝它们,这时使用引用形参时明智的选择。又因为无需改变 string 对象的内容,所以可以将形参定义成对常量的引用:
1 bool is_shorter(const string s1, const string s2){ 2 return s1.size() < s2.size(); 3 }
c++primer 5th:当函数无需修改引用形参的值时最好使用常量引用。
使用引用形参返回额外信息:
一个函数只能有一个返回值,若需要返回多个值的话,可以多传几个引用形参,因为引用形参的值能保留到对应的实参。