指针和引用

  • 格式:doc
  • 大小:54.00 KB
  • 文档页数:13

C/C++中指针和引用之相关问题研究

一、基本知识

指针和引用的声明方式:

声明指针: char* pc;

声明引用: char c = 'A'

char& rc = c;

它们的区别:

①从现象上看,指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。这句话可以理解为:指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。

②从内存分配上看,程序为指针变量分配内存区域,而不为引用分配内存区域,因为引用声明时必须初始化,从而指向一个已经存在的对象。引用不能指向空值。

③从编译上看,程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。这是使用指针不安全而使用引用安全的主要原因。从某种意义上来说引用可以被认为是不能改变的指针。

④不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。

⑤理论上,对于指针的级数没有限制,但是引用只能是一级。如下:

int** p1; // 合法。指向指针的指针

int*& p2; // 合法。指向指针的引用

int&* p3; // 非法。指向引用的指针是非法的

int&& p4; // 非法。指向引用的引用是非法的

注意上述读法是从左到右。

程序1:

#include "stdio.h"

int main(void)

{

// 声明一个char型指针pc,且让它指向空值

char* pc = 0;

char a = 'a';

// 声明一个引用rc,且让它引用变量a

char& rc = a;

printf("%d, %c\n", pc, rc);

char *pc2;

// 声明一个指针,但可以不初始化

pc2 = pc;

// char& rc2;

// 上面语句编译时,会产生如下错误:

// error C2530: 'rc2' : references must be initialized // 即,应用必须初始化

// rc = *pc;

// 上面语句编译不会有问题,但运行时,会报如下错误:

// "0x00401057"指令引用的"0x00000000"内存。该内存不能为"read"

// 说明引用在任何情况下,都不能指向空值

return 0;

}

程序2:

#include

#include

using namespace std;

int main(void)

{

string s1("Hello");

string s2("World");

// printf("%s\n", s1); 不能用printf输出s1,而应该用cout

cout << "s1的地址 = "<< &s1 << endl; // &s1 = 0012FF64

cout << "s2的地址 = "<< &s2 << endl; // &s2 = 0012FF54

string& rs = s1; // 1. 定义一个引用rs,rs引用s1

cout << "引用rs的地址 = " << &rs << endl; // &rs = 0012FF64

string* ps = &s1; //定义一个指针ps, ps指向s1

cout << "指针ps的地址 = " << ps << endl; // ps = 0012FF64

cout << rs << ", " << *ps << endl; // Hello, Hello

// 如果没有#include ,上面的语句在编译的时候,会出现如下错误:

// error C2679: binary '<<' : no operator defined which takes a right-

// hand operand of type 'class std::basic_string

// std::char_traits,class std::allocator >'

// (or there is no acceptable conversion)

rs = s2; // 2. rs仍旧引用s1, 但是s1现在的值是"World"

ps = &s2; // ps现在指向s2

cout << "引用rs的地址 = " << &rs << endl; // &rs = 0012FF64 未改变

cout << "引用rs的值 = " << rs << endl; // rs = "World" 已改变

cout << "指针ps的地址 = " << ps << endl; // ps = 0012FF54 已改变

cout << "指针ps所指地址的内容 = " << *ps << endl; // *ps = World 已改变

cout << "s1的地址 = "<< &s1 << endl; // 3. &s1 = 0012FF64 未改变

cout << "s1的值 = " << s1 << endl; // 4. s1 = World 已改变

return 0;

}

可以认为:

引用就是变量的别名,在引用初始化的时候就已经确定,以后不能再改变。见程序2的粗体字语句。第1句,声明了rs引用s1,s1的值为”Hello”,从这以后,rs实际上就相当于变量s1了,或者从更本质的意义上来说,rs的地址就是初始化时s1的地址了,以后都不会再改变。这应该比较好理解,比如我们在程序中定义了一个变量a,不管我们如何给a赋值,但它的地址是不会改变的;

第2句,rs仍旧指向初始化时s1的地址,但此处的赋值就相当于重新给s1赋值,因此我们从第3句和第4句可以看到,s1的地址并没有发生变化,但是其值已经发生了变化。

二、作为参数传递

利用引用的这个特性,可以用它作为函数的传出参数。如程序3:

#include

#include

using namespace std;

int newEvaluation(string& aStr)

{

string bStr("Hello,");

aStr = bStr + aStr;

return 0;

}

int main(void)

{

string aStr("Patrick!");

newEvaluation(aStr);

std::cout << aStr << endl; // 输出结果:"Hello, Patrick!"

return 0;

}

而一般变量,则不能从函数内部传值出来,比如程序4:

#include

#include

using namespace std;

int newEvaluation(string aStr)

{ string bStr("Hello,");

aStr = bStr + aStr;

return 0;

}

int main(void)

{

string aStr("Patrick!");

newEvaluation(aStr);

std::cout << aStr << endl; // 输出结果:"Patrick!",aStr的值没有变化

return 0;

}

当然程序3引用传递的方式也可以写成指针传递的方式,如程序5:

#include

#include

using namespace std;

int newEvaluation(string* const aStr)

{

string bStr("Hello,");

*aStr = bStr + *aStr;

return 0;

}

int main(void)

{

string aStr("Patrick!");

newEvaluation(&aStr);

std::cout << aStr << endl; // 输出结果:"Hello, Patrick!"

return 0;

}

注意程序中的陷井,如程序6:

#include

int *pPointer;

void SomeFunction()

{

int nNumber;

nNumber = 25;