指针和引用困扰了我好长一段时间,在函数内部使用熟练之后,发现把它们作为参数传递时,又有点懵了。所以在我把他搞明白之后,就打算写这篇文章来帮助一些初学者,看完这篇之后,你一定会彻底把它搞明白。
本文分两步来解释这两个概念的区别,第一是基本概念,第二是用案例说明在参数传递时的区别。
1. 基本概念
指针:
string a = "hello";
定义格式:string* p;
含义:是一个变量,通过p = 某地址(如p = &a)来改变此变量的值,从而使此变量指向不同的地址。指针变量的本质在于,它是指向某个地址的,地址前面加*表示该地址的内容,相当于一个字符串变量(或整型等)。
初始化:string* p = &a。指针可以不初始化,不初始化时为空指针,不能参与运算、输出、传递。
引用:
string a = "hello";
定义格式:string& b = a; 注意b前面有&符号,而a没有
含义:引用相当于取别名。b与a本质为同一个变量,他们有相同的地址,修改此地址上的内容,a、b一起变化;
初始化:定义时就必须初始化,否则会报错。例如,下面这种写法是错误的
string& b;
b = a;
2.举例说明(注意看注释)
首先,定义下面5个函数(申明)
#include<iostream>
using namespace std;
void fun1(string s); //最基础的用法,直接传递参数
void fun2(string* s); //传递指针,2和3做对比
void fun3(string* s);
void fun4(string& s); //传递引用,4和5做对比
void fun5(string& s);
其次,main函数,依次执行5个函数
int main()
{
string fun1_str = "hello";
cout << "fun1" << endl;
fun1(fun1_str);
cout << "str = hello ,执行函数后str = " << fun1_str << endl << endl;
cout << "fun2" << endl;
string fun2_str = "hello";
fun2(&fun2_str);
cout << "str = hello ,执行函数后str = " << fun2_str << endl << endl;
cout << "fun3" << endl;
string fun3_str = "hello";
fun3(&fun3_str);
cout << "str = hello ,执行函数后str = " << fun3_str << endl << endl;
cout << "fun4" << endl;
string fun4_str = "hello";
fun4(fun4_str);
cout << "str = hello ,执行函数后str = " << fun4_str << endl << endl;
cout << "fun5" << endl;
string fun5_str = "hello";
fun5(fun5_str);
cout << "str = hello ,执行函数后str = " << fun5_str << endl << endl;
}
然后,5个函数的内容如下
void fun1(string s) //形参为正常变量,只在函数内起作用,非常容易理解
{
string temp = "xyz"; //s和temp是两个没有任何关系的变量,都只在fun1中起作用,与main中没有任何关联
s = temp; //两个变量内容相同,但是没有任何关联,修改一个,不会影响另一个
s = "abc"; //改变形参值,但不影响传入的值
}
void fun2(string *s) //形参为指针变量,需传入一个地址,能影响外部
{
string temp = "xyz"; //temp是fun2中的变量,只在fun2中起作用
s = &temp; //将指针s指向temp,s不再指向main函数中的str,str保持原来的值hello,此函数内已无法修改main中的str
*s = "pqr"; //指针s所指的位置为temp,将该位置里面的内容变成了pqr,temp也会变成pqr
cout << "函数内部:fun2函数中的temp = pqr ? 验证:" << temp << endl;
}
void fun3(string *s) //与fun2相比,交换了函数中第2和第3行
{
string temp = "xyz"; //temp是fun2中的变量,只在fun2中起作用
*s = "pqr"; //指针s所指的位置为main中的str,将该位置里面的内容变成了pqr,main中的str也会变为pqr
s = &temp; //将指针s指向temp,s不再指向main函数中的str,main中的str保持为pqr,指针所指位置的内容为xyz
cout << "函数内部:fun3函数中的指针所指位置的内容为*s = xyz ? 验证:" << *s << endl;
}
void fun4(string& s) //形参为引用型,main中的str与此处的s为同一个变量(地址相同)
{
string temp = "xyz";
s = temp; //变量s变为xyz,main中的str也会变为xyz,但是s与temp没有关联
}
void fun5(string& s) //形参为引用型,main中的str与此处的s为同一个变量(地址相同)
{
string temp1 =s; //temp1与s是两个独立的变量。s既是此函数中的变量,又是main中的变量str
cout << "函数内部:fun5函数中的temp1 = hello ? 验证:" << temp1 << endl;
string *temp2 = &s; //temp2是一个指针,指向s的地址,而s的地址就是main中str的地址,因此*temp2为hello
temp1 = "pqr"; //temp1与s没有关联,不影响s,也不影响main中的str
*temp2 = "xyz"; //将xyz放入temp2所指的地址,因此main中的str会变成xyz,此函数中的s也会变成xyz
cout << "函数内部:fun5函数中的s = xyz ? 验证:" << s << endl;
}
接下来,看一下运行结果
fun1
str = hello ,执行函数后str = hello
fun2
函数内部:fun2函数中的temp = pqr ? 验证:pqr
str = hello ,执行函数后str = hello
fun3
函数内部:fun3函数中的指针所指位置的内容为*s = xyz ? 验证:xyz
str = hello ,执行函数后str = pqr
fun4
str = hello ,执行函数后str = xyz
fun5
函数内部:fun5函数中的temp1 = hello ? 验证:hello
函数内部:fun5函数中的s = xyz ? 验证:xyz
str = hello ,执行函数后str = xyz
E:\files\test\x64\Debug\test.exe (进程 17248)已退出,代码为 0。
按任意键关闭此窗口. . .
最后,总结
传递指针
指针作为参数传递时,函数的定义格式如下:
void fun(string* p){ ;}
调用fun时,需用取值符&传入一个地址,不能取常量地址(如&"hello"错的),格式如下:
string str = "hello";
fun(&str);
传递的是str的地址,指针p指向该地址,因此传递到函数中之后:
如果不对指针p重新赋值,那它依旧是指向main函数中的那个变量str,此时若对*p赋值某个字符串,则可以改变main函数中的str;
如果对指针p重新赋值为某个地址,则指针指向了其他位置,将会丢失与main函数中变量str的指向关系,在函数中将再也找不到main函数中str的位置;此时再对*p赋值某个字符串,将不会影响main函数中的str;
传递引用
引用作为参数传递时,函数的定义格式如下:
void fun(string& b){ ;}
调用fun时,直接传入字符串变量即可,注意不能传递常量(如"hello"错的),格式如下:
string str = "hello";
fun(str);
与常规的参数传递不同,这里传递的是str本身,fun函数中的形参b与main函数中的str是同一个变量,地址是相同的:
如果对形参s赋值某个字符串,则main函数中的str也会被赋值为该字符串,形参s与main函数中的str本质上就是同一个变量,拥有相同的地址,只是名称不同,所以引用又叫取别名。