C++程序设计 第八章 指针和引用

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
注意,赋值运算还隐含在函数调用时,实参值传递给形参也是一种赋值。
2
对于一个指针变量,就能通过它的值来访问对应地址的值。这种由地址求内容的过程就是间接引用运算。“*”作为一种单目运算符,作用于一个指针变量,就是按该指针的地址求值(英文称dereference,含义是去引用,习惯称为间接引用)。例如:
int a = 16, b = 28;
*d = *d * *d;//C等价于*d =(*d)*(*d);
}
void main(){
double d1 = 2.2;//D
square(&d1);//E
cout<<d1<<endl;//F
}
上面A行定义了一个函数square,其形参为一个double型指针。此函数对指针所指向的某个double变量求平方。此函数没有返回值。B行先判断形参指针不为空,这是指针作为形参的通常处理方法,目的是防止通过空指针进行有害操作。C行包含了间接引用运算符、乘法运算符和赋值运算符,完成平方计算。注意C行并没有改变指针d,而是改变了指针d所指向的调用方实参的值。
NULL是一个宏,其值为0,说明在多个头文件中,例如<iostream.h>中包含的<ios.h>。值为NULL的指针称为空指针。当一个指针变量暂时无法确定其指向或暂时不用时,可将它赋予NULL。在使用一个指针前应判定不能为空,以保证程序正常执行。
在为指针赋值时应保证指针类型的一致性。例如,你不能把一个int变量的地址赋给一个float指针,反之也不行。即便你使用强制类型转换能通过编译,也会在运行时出现错误。
至此,“*”有三种不同的含义,随其所作用的对象及位置的不同而不同。例如:
int a = 16, b = 28;
int *pa = &a, *pb;//*表示pa、pb是指针变量,指针说明
a *= a * b;//*表示乘法运算,双目运算符
*pa = 123;//*表示间接引用pa所指向的对象a,单目运算符
其中,<类型名>是这个指针变量所指向的对象的类型,简称指针类型,它可以是任何一种类型。*表示这个变量是一个指针变量。这个变量的类型就是“<类型名>*”。<变量名>是一个标识符。指针变量可以进行初始化,等号之后给出一个变量的地址,要求这个变量的类型与指针类型相符。
假设程序中说明了一个变量int i = 4,而且在运行时该变量i的地址为0X0012FF70。
int* p1;// *与类型名之间没有空格,与变量名p1之间有空格
int*p2;// *与类型名和变量名p2之间都没有空格
int * p3;// *与类型名和变量名p3之间都有空格
在说明一个指针变量后,无论该指针变量指向何种类型的对象,系统都为其分配4个字节大小的存储空间,因为指针变量存放内存单元的地址,在32位计算机中内存地址的取值范围是相同的,都是4个字节。
怎样能知道一个变量在运行时刻的内存地址?把取地址运算符&放在变量前面就得到它的首地址。例如b是一个变量,那么&b就表示它的地址。下面例子能看到一组局部变量的首地址。
例8-1显示一组局部变量的首地址。
#include<iostream.h>
void main(){
bool b = true;
char c = 'c';
首先,这6个变量的地址是按递减次序排列,这是因为局部变量都存储在堆栈中,堆栈是先入后出的。先入栈的数据存放在较大地址位置,后入栈的数据存放在较小地址位置。如果这些变量改为全局变量,它们的排列次序就会颠倒过来。
其次,尽管变量s只占2字节,变量c只占1字节,但却分别占用4字节空间。这是因为按字对齐(32位数据)能提高CPU访问内存的效率,而且一次压栈和出栈操作也是以32位数据为单位,代价是浪费一些内存。如果这些变量改为全局变量,它们将按实际大小存储。
指针即内存单元的地址,而数据是内存单元中的内容(或值)。
假设在程序中说明了1个int型的变量a,其值为68。系统为变量a分配4字节的存储空间,设首地址为0X0065FDF4。通过地址0X0065FDF4就能找到变量a在内存中的存储单元,从而对变量a进行访问。0X0065FDF4就是变量a的指针。知道一个变量的地址和变量的类型就能对变量进行访问,就如同知道房间号就能找到房间,从而找到房间里的主人。
如果一个指针变量存放的是某个对象或值的地址,就说这个指针变量指向该对象或值。在C++程序设计中,指针变量只有确定了指向才有意义。
8
指针变量就是专门存放地址的一种特殊变量。指针变量中存放的是地址值。一个指针的值就是一个地址。
指针变量与其它变量一样,在使用之前必须先说明。说明指针变量的格式为:
<类型名> *<变量名> [= &<变量>];
说明一个指针变量:
int * pa = &i;
此时指针变量pa中就存放了变量i的地址,即pa中存放的值为0X0012FF70。我们称pa指针指向了变量i。如图8.2所示。
现在访问变量i就有两种方式:一是按变量名i来访问。将变量名i转换为一个相对地址,在运行时经动态定位得到i的地址0X0012FF70,再找到i的存储单元。二是通过指针变量pa来访问。按pa变量的地址先找到pa,然后根据pa的值再找到变量i的存储单元,从而对变量i进行访问。前一种访问方式称为直接寻址,后一种称为间接寻址。间接方式的好处是一个指针pa在不同时刻可指向不同的整数变量,这样通过一个指针变量就能访问多个数据。
C++

在程序运行时变量和函数都存放在内存中,通过变量名来访问数据、通过函数名来调用函数都是直接访问方式。还有另一种间接访问方式就是用指针。指针的本质是内存地址。指针往往用于说明函数的形参,使实参能通过指针传递,以提高函数调用的效率。利用指针能动态地使用内存,以提高内存使用效率。指针也能用来表示数据关联,以构成复杂的数据结构。指针是C程序中最常见的类型。引用是C++扩展的新概念,主要用于函数形参和返回类型。本章将详细介绍指针和引用的概念及应用。
如果指针pa的类型是“int *”,那么“*pa”的类型就是int,这就是去引用的含义。
“&”所表示的求址运算与“*”所表示的间接引用运算是一对互逆的运算:
对于任一个变量v,“*&v”表达式等价于v,即*&v==v。
对于任一个指针变量p,“&*p”表达式等价于p,即&*p == p。
这是两个重要的等价式,将用于后面的数组访问。
float x =32.6f, y =69.1f;//说明浮点型变量x,y
int *pa, *pb = &b;//说明两个指向int对象的指针变量pa,pb,并使pb指向变量b
float *px, *py = NULL;//说明两个指向float对象的指针变量px,py,使py为空指针
px = &x;//使指针px指向变量x
int *pa, *pb = &b;
cout<<*pb<<endl;//输出pb所指对象b的值28
pa = &a;
cout<<a<<endl;//输出a的值16
*pa = 32;//修改pa所指对象a的值,使a的值变成32
cout<<a<<endl;//输出a的值32
从上面的例子可以看出,“*指针变量”既可作为左值,也可作为右值。作为左值可以修改指针所指对象的值,作为右值只能读取指针所指对象的值。
指针的一个重要用途是定义函数形参。在调用函数时所提供的实参(即主调方某变量的地址)必须符合形参的指针类型,才能将实参赋值给形参。函数内通过形参指针就能访问主调方的变量。
例8-2指针作为函数形参。
#include<iostream.h>
voidsquare(double *d){//A
if (d != NULL)//B
1
一个指针变量如果既没有初始化,也没有赋值,那它的值就是随机值,指向就不确定。如果此时使用指针变量,就会访问不确定的存储单元,可能有很大危害。因此指针变量在使用之前必须有确定的指向,通过给指针赋值就可以使之指向确定的数据。
下面例子说明如何给指针赋值,以及应注意的一些问题。
int a = 16, b = 28;//说明整型变量a,b
short s = 3;
int i = 4;
float f =1.0f;
double d = 1.Biblioteka Baidu;
cout<<"&b="<<&b<<endl;
cout<<"&c="<<hex<<(int)&c<<endl;
cout<<"&s="<<&s<<endl;
cout<<"&i="<<&i<<endl;
cout<<"&f="<<&f<<endl;
例如:
int *ip1, i2;//说明一个指针变量ip1(其类型为int *)和一个int变量i2
float *fp;//说明一个指针变量fp(其类型为float *)
指针变量ip1所指向的变量的类型为int型,指针变量fp所指向的变量的类型为float型。
下面说明指针变量的几种写法都是合法的。
int *p;// *与类型名之间有空格,与变量名p之间没有空格
8.1
指针(pointer)的本质是内存地址。指针变量就是专门存储地址的一种变量。通过指针变量所存储的地址来访问数据是一种间接寻址方式。由于处理器的机器语言能支持间接寻址,所以使用指针可以达到较高的计算性能。
8
C++编译器对不同对象或变量按其数据类型分配合适大小的存储空间。例如为char或bool型变量分配1个字节(bytes)的存储空间,short分配2字节,int和float分配4个字节,为double型变量分配8个字节的存储空间。当程序执行时,代码和变量都加载到内存中。计算机内存被分成若干个存储单元,存储单元以字节为单位。每个存储单元都有一个固定的编号,这个编号就是内存地址。尽管一个变量可能占用多个字节空间,但都通过第一个字节的地址来访问。存放某个变量的第一个字节的地址就是该数据的首地址。
cout<<"&d="<<&d<<endl;
}
执行程序,输出如下:
&b=0x0012FF7C
&c=12ff78
&s=0x0012FF74
&i=0x0012FF70
&f=0x0012FF6C
&d=0x0012FF64
注意在不同机器上或不同时刻运行,显示的地址都不一样。但我们能看到局部变量在内存中的一些排列规律。如图8.1所示。
同一个变量在不同机器上执行或在不同时刻执行,其地址都不一样。这是因为在加载一个程序时,系统根据当前可用内存来确定使用哪一块内存。在编程中一个具体的地址值没有多大意义,不应该直接用一个地址常量来为一个指针赋值,这是因为在运行时你不知道这个地址中当前存放的是什么。如果你通过这个地址读取它的内容,将得到不可预知的结果。如果你改变了它的内容,就会导致不可预知的后果,甚至导致严重错误而退出。所以对指针的操作应小心谨慎。
*pa = &b;//非法,左值与右值的类型不同,左值是int型,右值是int*型
pa = pb;//pa和pb都指向同一个变量
pa = &x;//非法,pa指向对象的类型只能是int型,而x是float型
pb = 0x3000;//非法,不能用字面常量给指针变量赋值
在一个变量名之前加一个“&”,就是取得该变量的地址,称为求址运算(address-of)。例如,“&b”是一个表达式,就是求变量b的地址。如果b的类型是int,那么表达式&b的类型就是“int *”,以此类推。这里“&”是一个单目运算符。前面第3章介绍的“&”是一个双目运算符,就是按位逻辑与运算符。本章后面还将介绍“&”的第三种用法,用来说明引用变量。
指针是一种特殊的数据类型。所有类型的变量,无论是基本类型、用户定义类型、还是这些类型的数组,在一次运行时都有确定的地址,因此它们都有指针。对于32位计算机,地址长度就是32位,因此一个指针需要4个字节,与整型int、浮点型float具有相同大小的长度。一个指针不仅有值,而且还要确定其类型,表示它能指向什么类型的数据,决定了通过它要取用多少字节作为该变量的值。
在一个变量说明语句中,或者一个函数形参表中,一个变量或形参之前加一个“*”,就说明了该变量或该形参是一个指针类型。注意这里的“*”是一个说明符号,并非一个运算符,区别于“*”作为双目乘法运算符。下面还将介绍“*”作为单目运算符的第三种用法。
8
既然指针就是地址,那么指针的运算实际上就是地址的运算。但由于地址是特殊的数据,使指针所能进行的运算受到一定限制。对于指针只能进行赋值运算、间接引用运算、算术运算、两个指针间的减运算和关系运算。
相关文档
最新文档