指针与引用

指针与引用

指针是一个变量,此变量存储的是地址值,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。指针比引用的功能强大,引用比指针更加安全及易使用。

1、性质辨析
1)相同点
地址范畴,通过使用内存地址来实现,从而间接使用对象。


2)不同点

(1)指针的值初始化后可以被改变;引用只能在定义时被初始化一次,之后可以改变指向的对象,但是仍然指向初始化时的对象

eg:

#include <iostream>

using std::cout;
using std::endl;

int main(int argc, char* argv[]) 
{
	int x = 10;
	int y = 11;
	int* p; //定义指针,可以不进行初始化

	p = &x;
	cout << "pointer initialize: " << *p << endl;

	p = &y;
	cout << "pointer change: " << *p << endl;

	cout << x << ","<< y << endl;

	int i = 1;
	int j = 2;
	int &a = i; //定义引用必须初始化,引用指向一个变量
	cout << "reference initialize: " << a << "," << i << endl;

	a = j; //引用指向另一个变量
	cout << "reference change: "<< a << ","  << i << endl;

	return 0;
}

运行结果:

pointer initialize: 10
pointer change: 11
10, 11
reference initialize: 1,1
reference change: 2,2


(2)指针有const,常量指针的指针指向对象不能通过其来操作,但可以通过原声明进行更改,常量指针本身是个变量,其可以不赋初值,且可被重新赋值;指针常量的值是指针,声明时必须赋值,赋值后,不可以指向别的地址;但它的指向对象可变;引用const从一而终,不可改变指向的对象

eg:

#include <iostream>

using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
	int x = 10;
	const int * p1;//常量指针 int const * p1
	p1 = &x;
	cout << "const pointer: " << endl;
	cout << p1 << "," << *p1 << "," << x << endl;
	
	//*p1 = 20;
	x = 20;
	cout << p1 << "," << *p1 << "," << x << endl;

	p1+1;
	cout << p1 << "," << *p1 << "," << x << endl;

	int y = 11;
	int * const p2 = &y; //指针常量
	cout << "pointer const: " << endl;
	cout << p2 << "," << *p2 << "," << y << endl;

	int w = 22;
	*p2 = w;
	cout << p2 << ","<<  *p2 << ","  << y << endl;

	int i = 1;
	int &a = i;
	cout << a << "," << i << endl;

	a = 10;
	cout << a << "," << i << endl;

	int j = 2;
	const int &b = j;
	cout << b << "," << j << endl;
               //b = 12;

	return 0;
} 

注:

                      


(3)指针可以有多级;引用只可以有一级

eg:

#include <iostream>

using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
	int x = 0;
	int y = 0;
	int *p = &x;
	int *q = &y;
	int **pp = &p;
	pp = &q;//*pp = q
	**pp = 4;
	cout << q << "," << *q << endl;
	cout << pp << "," << *pp << "," << **pp << "," << endl;
	cout << &y << endl;
	cout << x << "," << y << endl;

	//int i = 1;
	//int &&p = i;
	return 0;
}

运行结果:

002CF904,4
002CF8EC,002CF904,4
002CF904
0,4



(4)为得到一个指针,通常需要使用new或&;为访问一个指针指向的对象,需使用*或[];对于结构体和类,指针使用->操作成员,引用使用.

  • 指针传递

a、 减少数据分配的时间和空间,提高程序运行的效率;

b、在被调函数中,可直接改变实参对象的值,实现函数之间的信息交换。

eg:

#include <iostream>

using std::cout;
using std::endl;

class CPoint 
{
public:
	CPoint(int x, int y);
	void copy(CPoint *point); 
	void setXY(int x, int y);
	void disp();
private:
	int m_x;            
	int m_y;
};

CPoint::CPoint(int x, int y)
{
	m_x = x;
	m_y = y;
}

void CPoint::copy(CPoint *point)
{
	m_x = point->m_x;          
	m_y = point->m_y;                 
}

void CPoint::disp()
{
	cout << "x=" << m_x << ";y=" << m_y << endl;
}

void CPoint::setXY(int x, int y) 
{
	m_x = x;
	m_y = y;
}

void func(CPoint *p) 
{           
	p->setXY(55, 33);
}


int main(int argc, char* argv[])
{
	CPoint p1(5, 5), p2(25, 25);

	p1.copy(&p2);              
	p1.disp();

	func(&p2);              
	p2.disp();

	return 0;
}

运行结果:

x=25;y=25
x=55;y=33

  • 引用传递

不仅具有对象指针的优点,而且使用对象引用更为直观和简单。

eg:

#include <iostream>

using std::cout;
using std::endl;

class CPoint 
{
public:
	CPoint(int x, int y);
	void copy(CPoint &point); 
	void setXY(int x, int y);
	void disp();
private:
	int m_x;            
	int m_y;
};

CPoint::CPoint(int x, int y)     
{
	m_x = x;
	m_y = y;
}

void CPoint::copy(CPoint &point) 
{
	m_x = point.m_x;          
	m_y = point.m_y;
}

void CPoint::disp() 
{
	cout << "x=" << m_x << ";y=" << m_y << endl;
}

void CPoint::setXY(int x, int y) 
{
	m_x = x;
	m_y = y;
}

void func(CPoint &p) 
{           
	p.setXY(55, 33);
}

int main(int argc, char* argv[])
{
	CPoint p1(5, 5), p2(25, 25);
	p1.copy(p2);             
	p1.disp();
	func(p2);              
	p2.disp();
	return 0;
}

运行结果:

x=25;y=25
x=55;y=33

(5) 指针可以被直接赋值为空;引用的值不可以,但可以间接,如果改变它的值,程序基本会崩溃 

eg:

#include <iostream>

using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
	int *p = NULL;
	//int &r = NULL; 
	int &r = *p;
	r = 1;
	return 0;
}

    


(6)指针的算术操作是改变指针的指向;引用直接改变引用对象的值

eg:

#include <iostream>
#include <string>

using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
	int a[5] = { 1, 3, 4, 5, 6 };
	int *p = &a[0];
	cout << a[0] << "," << *(p + 1) << endl; //指向下一个变量
	int &ref = a[0];
	cout << a[0] << "," << (ref + 1) << endl; //变量本身
	return 0;
}

运行结果:

1,3

1,2


(7)指针的大小是指针本身的大小,对于32位系统,其为4bytes;引用的大小是指向变量的大小

eg:

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
	string str = "123456";
	string& ref = str;
	string *pointer = &str;

	cout << ref << "\t" << *pointer << endl;
	cout << sizeof(ref) << "\t" << sizeof(pointer) << endl;

	return 0;
}

运行结果:

123456   123456
28       4


(8)为指针赋值,赋值给指针自身;为引用赋值,执行深度复制,赋值给引用对象

(9)指针的效率不如引用,指针必须测试合法性,且指针传递的参数,需创建它的副本;引用不需要,引用就是它本身

eg:

void printDouble(const double *pd)  
{  
if (pd) {             // check for null pointer  
cout << *pd;  
}  
}  

void printDouble(const double& rd)  
{  
    cout << rd;         // no need to test rd; it  
}                       // must refer to a double 



2、实现
C++标准没有指定引用的实现方式,但是编译器基本上通过指针的方式实现引用。如果没有进行优化,指针与引用占有同样的内存空间。

eg:

#include <iostream>

using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
	int i = 1;

	int& ref = i;
	cout << "ref = " << ref << endl;

	int *p = &i;
	cout << "*p = "  << *p << endl;

	return 0;
}
反汇编代码:

--- f:\workspace\test\test\main.cpp --------------------------------------------
#include <iostream>


using std::cout;
using std::endl;


int main(int argc, char* argv[])
{
00882420  push        ebp  
00882421  mov         ebp,esp  
00882423  sub         esp,0E4h  
00882429  push        ebx  
0088242A  push        esi  
0088242B  push        edi  
0088242C  lea         edi,[ebp-0E4h]  
00882432  mov         ecx,39h  
00882437  mov         eax,0CCCCCCCCh  
0088243C  rep stos    dword ptr es:[edi]  
int i = 1;
0088243E  mov         dword ptr [i],1  


int& ref = i;
00882445  lea         eax,[i]  
00882448  mov         dword ptr [ref],eax  

cout << "ref = " << ref << endl;
0088244B  mov         esi,esp  
0088244D  push        offset std::endl<char,std::char_traits<char> > (088107Dh)  
00882452  mov         edi,esp  
00882454  mov         eax,dword ptr [ref]  
00882457  mov         ecx,dword ptr [eax]  
00882459  push        ecx  
0088245A  push        offset string "ref = " (0888B30h)  
0088245F  mov         edx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (088C098h)]  
00882465  push        edx  
00882466  call        std::operator<<<std::char_traits<char> > (088136Bh)  
0088246B  add         esp,8  
0088246E  mov         ecx,eax  
00882470  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (088C0A4h)]  
00882476  cmp         edi,esp  
00882478  call        __RTC_CheckEsp (088114Fh)  
0088247D  mov         ecx,eax  
0088247F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (088C0A8h)]  
00882485  cmp         esi,esp  
00882487  call        __RTC_CheckEsp (088114Fh)  


int *p = &i;
0088248C  lea         eax,[i]  
0088248F  mov         dword ptr [p],eax  
cout << "*p = "  << *p << endl;

00882492  mov         esi,esp  
cout << "*p = "  << *p << endl;
00882494  push        offset std::endl<char,std::char_traits<char> > (088107Dh)  
00882499  mov         edi,esp  
0088249B  mov         eax,dword ptr [p]  
0088249E  mov         ecx,dword ptr [eax]  
008824A0  push        ecx  
008824A1  push        offset string "*p = " (0888B38h)  
008824A6  mov         edx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (088C098h)]  
008824AC  push        edx  
008824AD  call        std::operator<<<std::char_traits<char> > (088136Bh)  
008824B2  add         esp,8  
008824B5  mov         ecx,eax  
008824B7  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (088C0A4h)]  
008824BD  cmp         edi,esp  
008824BF  call        __RTC_CheckEsp (088114Fh)  
008824C4  mov         ecx,eax  
008824C6  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (088C0A8h)]  
008824CC  cmp         esi,esp  
008824CE  call        __RTC_CheckEsp (088114Fh)  


return 0;
008824D3  xor         eax,eax  
}


3、参数传递

  • 指针传递:本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为实参的一个副本。被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。(拷贝)
  • 引用传递:被调函数的形式参数作为局部变量,在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量,节约时间与空间。被调函数对形参做的任何操作都影响了主调函数中的实参变量。(本身)
传值方式选择
  • 对于小对象,倾向于值传递
  • 对于没有对象是有效函数,使用指针参数(NULL测试)
  • 否则,使用引用参数

(1)指针与引用

1)变量名传参

传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。

eg:

#include <iostream> 

using std::cout;
using std::endl;

void swap(int, int);

int main(int argc, char* argv[])
{
	int i = 3;
	int j = 5;
	swap(i, j); 
	cout << i << " " << j << endl; 
	return 0;
}

void swap(int a, int b) 
{
	int temp;
	temp = a;  
	a = b;
	b = temp;
}

运行结果:

3 5



2)指针传参

形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。

这种虚实结合的方法仍然是“值传递”方式,只是实参的值是变量的地址而已。通过形参指针变量访问主函数中的变量,并改变它们的值。

eg:

#include <iostream> 

using std::cout;
using std::endl;

void swap(int *, int *);

int main(int argc, char* argv[])
{
	int i = 3, j = 5;
	swap(&i, &j); 
	cout << i << " " << j << endl; 
	return 0;
}

void swap(int *p1, int *p2) 
{
	int temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

运行结果:

5 3


3)引用传参

eg:

#include <iostream> 

using std::cout;
using std::endl;

void swap(int &, int &);

int main(int argc, char* argv[])
{
	int i = 3;
	int j = 5;
	swap(i, j); 
	cout << i << " " << j << endl; 
	return 0;
}

void swap(int &a, int &b) 
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}

运行结果:

5 3 


2)指针的指针及引用指针

a、指针

eg:

#include <iostream>

using std::cout;
using std::endl;

//global variable
int g_One = 1;

//function prototype
void func(int* pInt);

int main(int argc, char* argv[])
{
	int nvar = 2;
	int* pvar = &nvar;
	func(pvar);
	cout << *pvar << endl;    //Will still show 2
	return 0;
}

void func(int* pInt)
{
	pInt = &g_One;
}

运行结果:

2


b、指针的指针

eg:

#include <iostream>

using std::cout;
using std::endl;

//global variable
int g_One = 1;

//function prototype
void func(int** ppInt);

int main(int argc, char* argv[])
{
	int nvar = 2;
	int* pvar = &nvar;
	func(&pvar);
	cout << *pvar << endl;    //Will still show 2
	return 0;
}

void func(int** ppInt)
{
	//Modify the pointer ppInt points to
	*ppInt = &g_One;
	//You can also allocate memory, depending on your requirements
	//*ppInt = new int;
	//Modify the variable *ppInt points to
	//**ppInt = 3;
}

运行结果:

1

ppInt: 指针的指针, 不会去对它做修改,否则会丢失这个指针指向的指针地址
*ppInt: 被指向的指针,是一个地址。如果我们修改它,修改的是被指向的指针的内容,即修改main()中*pn指针
**pInt: 两次解引用,修改main()中*pn内容

c、引用指针

eg:

#include <iostream>

using std::cout;
using std::endl;

//global variable
int g_One = 1;

//function prototype
void func(int*& rpInt);

int main(int argc, char* argv[])
{
	int nvar = 2;
	int* pvar = &nvar;
	func(pvar);
	cout << *pvar << endl;    //Will still show 2
	return 0;
}

void func(int*& rpInt)
{
	//Modify the pointer ppInt points to
	rpInt = &g_One;
	//You can also allocate memory, depending on your requirements
	//rpInt = new int;
	//Modify the variable *ppInt points to
	//*rpInt = 3;
}

运行结果:

1

rpInt :指针引用,main()中的*pn
*rpInt:main()中*pn指向的内容

参考文献
[1] Bjarne Stroustrup(美)著, 王刚 刘晓光 吴英 李涛 译. C++程序设计原理与实践[M]. 指针与引用:359~363.
[2] 浅谈C++中指针和引用的区别. http://www.cnblogs.com/dolphin0520/archive/2011/04/03/2004869.html. 
[3]  c++引用与指针的区别(着重理解). http://blog.csdn.net/thisispan/article/details/7456169. 
[4] C++中引用和指针的区别. http://blog.csdn.net/listening_music/article/details/6921608. 
[5] 指针和指针的引用. http://www.cnblogs.com/no7dw/archive/2011/03/19/1988540.html. 
[6] C++中的引用与指针的区别. http://www.cnblogs.com/tracylee/archive/2012/12/04/2801519.html. 
[7] What are the differences between a pointer variable and a reference variable in C++?. http://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in. 
[8] Distinguish between pointers and references in C++. http://www.cplusplus.com/articles/ENywvCM9/. 
[9] Pointer-to-Pointer and Reference-to-Pointer. http://www.codeguru.com/cpp/cpp/cpp_mfc/pointers/article.php/c4089/PointertoPointer-and-ReferencetoPointer.htm. 
[10] References vs. Pointers. http://www.embedded.com/electronics-blogs/programming-pointers/4023307/References-vs-Pointers. 
[11] 指针. http://baike.baidu.com/link?url=kkjEiU7LQ57TtU6tuS3o42V5KxapLkWvy9zws9C7ejdLH7c7kV1dDnfmwp-EULPSEtftCzah3g_NosbFFGMkD_. 
[12] 常量指针与指针常量的区别(转帖). http://www.cnblogs.com/witty/archive/2012/04/06/2435311.html. 
[13] C++新特性(类与对象的各种指针和引用. http://blog.csdn.net/pearl333/article/details/8027358. 


注:为了便于自己学习,无心侵权,尽可能将所引用的文章列举出来;有些文章的内容可能会与原作重复度较高,还请谅解。如作者举报,愿意删除此文。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值