指针与引用
指针是一个变量,此变量存储的是地址值,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。指针比引用的功能强大,引用比指针更加安全及易使用。
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.
注:为了便于自己学习,无心侵权,尽可能将所引用的文章列举出来;有些文章的内容可能会与原作重复度较高,还请谅解。如作者举报,愿意删除此文。