首页 > 代码库 > 从Java到C++——指针与引用
从Java到C++——指针与引用
从Java到C++——指针与引用
C++即有指针又有引用,而且很多时候具有相似的功能,常常容易让人混淆,着实让人头痛。用两句通俗的话说明两者本质的含意:引用就是一个变量或对象的别名(引用的本质是一个对象);指针是一个段内存空间的地址(指向存储一个变量值的空间或一个对象的空间);如下图所示:
C++中的引用与指针
引用
引用是为变量或对象起的另外一个别名,定义形式:T& v; T为类型,v为变量名。使用引用时,注意以下几点:
1.引用在创建的时候必须被初始化(指针可以在任何时候赋值)
2.一旦引用初始化后,就不能改变引用所指向的变量(或对象),一直和该变量(或对象)邦定在一起。
3.引用必须与一个确定的合法内存单元相关联。不存在NULL引用。(指针可以为NULL)
下面用例子来解释,【例1】:
void Reference() { //int &r1; //不符第1要点, 报错 Error:refercece value "r" requires an intializer //int & r2 = 100; //非const类型的引用,不能与常量邦定在一起 int i = 10; int &r3 = i; //引用r2与变量i邦定在一起,符合第1要点 cout << "初始值 i:" << i << " r3:" << r3 << endl; i = 15; //改变i的值,r3跟着改变,因为i和r3是指向同一块内存,符合第2要点 cout << "改变i i:" << i << " r3:" << r3 << endl; r3 = 20; //改变r3的值,i跟着改变,因为i和r3是指向同一块内存是,符合第2要点 cout << "改变r3 i:" << i << " r3:" << r3 << endl; int i2 = 25; r3 = i2; //并不是引用r3指向了变量i2,而是i2的值赋给了变量i(r3所指向的变量),符合第2要点 cout << "赋值r3 i:" << i << " r3:" << r3 << " i2:" << i2 << endl; int& r4 = r3; //用引用r3指向的变量i的值初始化引用r4,所用r3,r4邦定的是同一个变量i;r3、r4、i指向同一块内存 cout << "引用r4 i:" << i << " r3:" << r3 << " r4:" << r4 << endl; Data d; Data& rData = d; //也可对对象进行引用,原理与变量一样。 cout << "d.iVal:" << d.iVal << " rData.iVal:" << rData.iVal << endl; d.iVal = 8; cout << "d.iVal:" << d.iVal << " rData.iVal:" << rData.iVal << endl; } |
结果:
初始值 i:10 r3:10
改变i i:15 r3:15
改变r3 i:20 r3:20
赋值r3 i:25 r3:25 i2:25
引用r4 i:25 r3:25 r4:25
d.iVal:0 rData.iVal:0
d.iVal:8 rData.iVal:8
我们也可以用debug模式对它进行调试,这样可以看到各个变量的内存地址,如下:
指针
指针是指向另外一个类型的复合类型,定义形式:T* p; T为类型,p为指针变量。与引用类似,指针也实现了其它变量或对象的间接访问。使用指针变量p时,在p的前面加上*(即:*p)就是解引用符,用来访问指针所指向的对象。指针具有以下特点:
1.指针本身就是一个对象,允许对指针进行赋值和拷贝;
2.指针的生命周期内它可以先后指向几个不同的对象;
3.指针无须在定义时赋初值;
4.与内置类型一样,如果指针没有初始化,将指向一个不确定的值;
5.不能定义指向引用的指针(因为引用不是对象,没有实际的地址)。
【例2】
void TestPointer() { double* p1; //定义一个指向double类型的指针p1,指针未被初始化,可能指向任意一个地址 double dVal1 = 2; p1 = &dVal1; //给指针p1赋值, &是取地址符,也就是把dVal1的地址赋给p1 cout << "p1:" << p1 << " *p1:" << *p1 << endl; //*是解引用符,*p1也就是获取指针p1所指向的变量dVal1的值 double* p2 = p1; //定义指针p2,同时将p1的值赋给p2,这时p1和p2指向同一个地址 cout << "p1:" << p1 << " p2:" << p2 << " *p1:" << *p1 << " *p2:" << *p2 << endl; } |
结果:
p1:001AF6B8
*p1:2
p1:001AF6A8 p2:001AF6A8 *p1:5 *p2:5
我们也可以用debug模式对它进行调试,这样可以看到各个变量的内存地址,如下:
指针的值的状态
指针的值(即地址)应是下面四种状态之一:
1.指向一个对象(或内置类型变量);
2.指向紧邻对象所占空间的下一个位置;
3.空指针,意味着没有指向任何对象;
4.无效指针,也就是以上情况之外的其它值。
举例解释【例3】
void TestPointer2() { short sInt = 65; short *sP; //状态4,未被初始化,无效指针 //cout << sP << endl; //错误,将报异常:The variable ‘sP‘ is being used without being initialized. sP = &sInt; //状态1,指向变量ch cout << "cP:" << sP << " *cP:" << *sP << endl; sP ++; //状态2,指向变量sInt所在地址的下一个地址 cout << "cP:" << sP << endl; sP = NULL; //状态3,空指针 cout << "cP:" << sP << endl; } |
结果:
cP:0037FD54 *cP:65
cP:0037FD56
cP:00000000
易错点
对于初学者或是从其它语言转向c++的人来说,指针和引用这部分比较难,我觉得主要有以下几点,也是大家需要注意的:
1.&符号的多意性(多个用途)
&即用在引用也用在指针。定义引用变量,一般在“=”的左侧,在类型后加&符号(如果是在一行内定义多个引用,则每个变量名前加&);表示取变量地址,一般在“=”的右侧用于给指针赋值,&在变量名前面。
void TestSymbol() { int nVal1 = 5; int nVal2 = 6; int nVal3 = 9; int& rVal1 = nVal1; //定义引用 int &rVal2 = nVal2, &rVal3 = nVal3; //定义引用 cout << "rVal1:" << rVal1 << " rVal2:" << rVal2 << " rVal3:" << rVal3 << endl;
int *p1, *p2, *p3; p1 = &nVal1; //取nVal1的地址,给p1赋值 p2 = &nVal2, p3 = &nVal3; //取nVal2, nVal3地址,分别给p2、p3赋值 cout << "*p1:" << *p1 << " *p2:" << *p2 << " *p3:" << *p3 << endl; } |
2.引用和指针的定义
一行内定义多个引用,每个引用前都要加&;一行内定义多个指针,每个引用前都要加*。引用的类型要和它所邦定的变量或对象匹配;指针的类型要和它所指向的变量或对象匹配。
void ReferencePointer() { int nVal1 = 10; int nVal2 = 20; int &rVal1 = nVal1, &rVal2 = nVal2; cout << "rVal1:" << rVal1 << " rVal2:" << rVal2 << endl; int *p1 = &nVal1, *p2 = &nVal2, *p3 = &nVal2; //p2和p3指向同一个地址 cout << "*p1:" << *p1 << " *p2:" << *p2 << endl; double dVal = 10.125; //double& rDVal = nVal1; //类型不匹配,编译错误 //double *p = &nVal1; //类型不匹配,编译错误 } |
结果:
rVal1:10 rVal2:20
*p1:10 *p2:20