首页 > 代码库 > 从Java到C++——指针与引用

从Java到C++——指针与引用

JavaC++——指针与引用

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