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

2019-01-19 19:07

C++程序设计

第8章 指针和引用

在程序运行时变量和函数都存放在内存中,通过变量名来访问数据、通过函数名来调用函数都是直接访问方式。还有另一种间接访问方式就是用指针。指针的本质是内存地址。指针往往用于说明函数的形参,使实参能通过指针传递,以提高函数调用的效率。利用指针能动态地使用内存,以提高内存使用效率。指针也能用来表示数据关联,以构成复杂的数据结构。指针是C程序中最常见的类型。引用是C++扩展的新概念,主要用于函数形参和返回类型。本章将详细介绍指针和引用的概念及应用。

8.1 指针及指针变量

指针(pointer)的本质是内存地址。指针变量就是专门存储地址的一种变量。通过指针变量所存储的地址来访问数据是一种间接寻址方式。由于处理器的机器语言能支持间接寻址,所以使用指针可以达到较高的计算性能。 8.1.1 地址的概念

C++编译器对不同对象或变量按其数据类型分配合适大小的存储空间。例如为char或bool型变量分配1个字节(bytes)的存储空间,short分配2字节,int和float分配4个字节,为double型变量分配8个字节的存储空间。当程序执行时,代码和变量都加载到内存中。计算机内存被分成若干个存储单元,存储单元以字节为单位。每个存储单元都有一个固定的编号,这个编号就是内存地址。尽管一个变量可能占用多个字节空间,但都通过第一个字节的地址来访问。存放某个变量的第一个字节的地址就是该数据的首地址。

指针即内存单元的地址,而数据是内存单元中的内容(或值)。

假设在程序中说明了1个int型的变量a,其值为68。系统为变量a分配4字节的存储空间,设首地址为0X0065FDF4。通过地址0X0065FDF4就能找到变量a在内存中的存储单元,从而对变量a进行访问。0X0065FDF4就是变量a的指针。知道一个变量的地址和变量的类型就能对变量进行访问,就如同知道房间号就能找到房间,从而找到房间里的主人。

指针是一种特殊的数据类型。所有类型的变量,无论是基本类型、用户定义类型、还是这些类型的数组,在一次运行时都有确定的地址,因此它们都有指针。对于32位计算机,地址长度就是32位,因此一个指针需要4个字节,与整型int、浮点型float具有相同大小的长度。一个指针不仅有值,而且还要确定其类型,表示它能指向什么类型的数据,决定了通过它要取用多少字节作为该变量的值。

同一个变量在不同机器上执行或在不同时刻执行,其地址都不一样。这是因为在加载一个程序时,系统根据当前可用内存来确定使用哪一块内存。在编程中一个具体的地址值没有多大意义,不应该直接用一个地址常量来为一个指针赋值,这是因为在运行时你不知道这个地址中当前存放的是什么。如果你通过这个地址读取它的内容,将得到不可预知的结果。如果你改变了它的内容,就会导致不可预知的后果,甚至导致严重错误而退出。所以对指针的操作应小心谨慎。

怎样能知道一个变量在运行时刻的内存地址?把取地址运算符&放在变量前面就得到它的首地址。例如b是一个变量,那么&b就表示它的地址。下面例子能看到一组局部变量的首地址。

例8-1 显示一组局部变量的首地址。

#include void main(){

bool b = true; char c = 'c'; short s = 3; int i = 4;

float f = 1.0f; double d = 1.0;

cout<<\

cout<<\ cout<<\ cout<<\ cout<<\ cout<<\}

执行程序,输出如下:

&b=0x0012FF7C &c=12ff78

&s=0x0012FF74 &i=0x0012FF70 &f=0x0012FF6C &d=0x0012FF64

注意在不同机器上或不同时刻运行,显示的地址都不一样。但我们能看到局部变量在内存中的一些排列规律。如图8.1所示。

0012FF64d : 1.0f : 1.0fi : 4s : 3c : 'c'b : 1占8字节占4字节占4字节占4字节占4字节占4字节0012FF6C0012FF700012FF740012FF780012FF7C 图8.1 一组局部变量的存储地址 首先,这6个变量的地址是按递减次序排列,这是因为局部变量都存储在堆栈中,堆栈是先入后出的。先入栈的数据存放在较大地址位置,后入栈的数据存放在较小地址位置。如果这些变量改为全局变量,它们的排列次序就会颠倒过来。

其次,尽管变量s只占2字节,变量c只占1字节,但却分别占用4字节空间。这是因为按字对齐(32位数据)能提高CPU访问内存的效率,而且一次压栈和出栈操作也是以32位

数据为单位,代价是浪费一些内存。如果这些变量改为全局变量,它们将按实际大小存储。

如果一个指针变量存放的是某个对象或值的地址,就说这个指针变量指向该对象或值。在C++程序设计中,指针变量只有确定了指向才有意义。 8.1.2 指针变量

指针变量就是专门存放地址的一种特殊变量。指针变量中存放的是地址值。一个指针的值就是一个地址。

指针变量与其它变量一样,在使用之前必须先说明。说明指针变量的格式为: <类型名> *<变量名> [= &<变量>];

其中,<类型名>是这个指针变量所指向的对象的类型,简称指针类型,它可以是任何一种类型。*表示这个变量是一个指针变量。这个变量的类型就是“<类型名> *”。<变量名>是一个标识符。指针变量可以进行初始化,等号之后给出一个变量的地址,要求这个变量的类型与指针类型相符。

假设程序中说明了一个变量int i = 4,而且在运行时该变量i的地址为0X0012FF70。 说明一个指针变量:

int * pa = &i;

此时指针变量pa中就存放了变量i的地址,即pa中存放的值为0X0012FF70。我们称pa指针指向了变量i。如图8.2所示。

0012FF70pa : 0012FF70i : 4 图8.2 指针变量的值与其所指内容 现在访问变量i就有两种方式:一是按变量名i来访问。将变量名i转换为一个相对地址,在运行时经动态定位得到i的地址0X0012FF70,再找到i的存储单元。二是通过指针变量pa来访问。按pa变量的地址先找到pa,然后根据pa的值再找到变量i的存储单元,从而对变量i进行访问。前一种访问方式称为直接寻址,后一种称为间接寻址。间接方式的好处是一个指针pa在不同时刻可指向不同的整数变量,这样通过一个指针变量就能访问多个数据。

例如:

int *ip1, i2; float *fp;

//说明一个指针变量ip1(其类型为int *)和一个int变量i2

//说明一个指针变量fp(其类型为float *)

指针变量ip1所指向的变量的类型为int型,指针变量fp所指向的变量的类型为float型。

下面说明指针变量的几种写法都是合法的。

int *p; int* p1; int*p2; int * p3;

// *与类型名之间有空格,与变量名p之间没有空格 // *与类型名之间没有空格,与变量名p1之间有空格 // *与类型名和变量名p2之间都没有空格 // *与类型名和变量名p3之间都有空格

在说明一个指针变量后,无论该指针变量指向何种类型的对象,系统都为其分配4个字节大小的存储空间,因为指针变量存放内存单元的地址,在32位计算机中内存地址的取值范围是相同的,都是4个字节。

在一个变量说明语句中,或者一个函数形参表中,一个变量或形参之前加一个“*”,就说明了该变量或该形参是一个指针类型。注意这里的“*”是一个说明符号,并非一个运算符,区别于“*”作为双目乘法运算符。下面还将介绍“*”作为单目运算符的第三种用法。

8.1.3 指针的运算

既然指针就是地址,那么指针的运算实际上就是地址的运算。但由于地址是特殊的数据,使指针所能进行的运算受到一定限制。对于指针只能进行赋值运算、间接引用运算、算术运算、两个指针间的减运算和关系运算。 1.赋值运算

一个指针变量如果既没有初始化,也没有赋值,那它的值就是随机值,指向就不确定。如果此时使用指针变量,就会访问不确定的存储单元,可能有很大危害。因此指针变量在使用之前必须有确定的指向,通过给指针赋值就可以使之指向确定的数据。

下面例子说明如何给指针赋值,以及应注意的一些问题。

int a = 16, b = 28; //说明整型变量a,b 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 *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章介绍的“&”是一个双目运算符,就是按位逻辑与运算符。本章后面还将介绍“&”的第三种用法,用来说明引用变量。

NULL是一个宏,其值为0,说明在多个头文件中,例如中包含的。值为NULL的指针称为空指针。当一个指针变量暂时无法确定其指向或暂时不用时,可将它赋予NULL。在使用一个指针前应判定不能为空,以保证程序正常执行。

在为指针赋值时应保证指针类型的一致性。例如,你不能把一个int变量的地址赋给一个float指针,反之也不行。即便你使用强制类型转换能通过编译,也会在运行时出现错误。

注意,赋值运算还隐含在函数调用时,实参值传递给形参也是一种赋值。 2.间接引用运算

对于一个指针变量,就能通过它的值来访问对应地址的值。这种由地址求内容的过程就是间接引用运算。“*”作为一种单目运算符,作用于一个指针变量,就是按该指针的地址求值(英文称dereference,含义是去引用,习惯称为间接引用)。例如:

int a = 16, b = 28; int *pa, *pb = &b; cout<<*pb<

cout<

//输出pb所指对象b的值28

//输出a的值16

//修改pa所指对象a的值,使a的值变成32 //输出a的值32

从上面的例子可以看出,“*指针变量”既可作为左值,也可作为右值。作为左值可以修改指针所指对象的值,作为右值只能读取指针所指对象的值。

如果指针pa的类型是“int *”,那么“*pa”的类型就是int,这就是去引用的含义。

“&”所表示的求址运算与“*”所表示的间接引用运算是一对互逆的运算: ? 对于任一个变量v,“*&v”表达式等价于v,即*&v == v。 ? 对于任一个指针变量p,“&*p”表达式等价于p,即&*p == p。 这是两个重要的等价式,将用于后面的数组访问。 至此,“*”有三种不同的含义,随其所作用的对象及位置的不同而不同。例如:

int a = 16, b = 28; int *pa = &a, *pb; a *= a * b; *pa = 123;

//*表示pa、pb是指针变量,指针说明

//*表示乘法运算,双目运算符

//*表示间接引用pa所指向的对象a,单目运算符

指针的一个重要用途是定义函数形参。在调用函数时所提供的实参(即主调方某变量的地址)必须符合形参的指针类型,才能将实参赋值给形参。函数内通过形参指针就能访问主调方的变量。

例8-2 指针作为函数形参。

#include void square(double *d){ if (d != NULL) *d = *d * *d; }

void main(){

double d1 = 2.2; square(&d1); cout<

//A //B

//C 等价于*d = (*d) * (*d); //D //E //F

上面A行定义了一个函数square,其形参为一个double型指针。此函数对指针所指向的某个double变量求平方。此函数没有返回值。B行先判断形参指针不为空,这是指针作为形参的通常处理方法,目的是防止通过空指针进行有害操作。C行包含了间接引用运算符、乘法运算符和赋值运算符,完成平方计算。注意C行并没有改变指针d,而是改变了指针d所指向的调用方实参的值。

主函数main中在D行定义了一个局部变量d1。E行调用square函数,实参为&d1,即

maind1 : 2.2?call?d=&d1dsquare 图8.3 函数形参指向主调方的变量 变量d1的地址被赋值给函数形参d,使形参d指向变量d1。如图8.3所示。在执行函数square时就能读取并改变该变量的值。当函数返回后,F行输出的d1就是原值2.2的平方,4.84。 用指针作为函数形参的好处就是用传递地址来代替传递值。不管多大对象,传地址只需4字节,所以适合传递“大”对象给函数来处理,能提高计算效率。 3.与整数的算术运算

指针与整数之间的算术运算有两种:与整数的加减运算和自增自减运算。这些运算只在访问某个数组时才有用。

(1)与整数的加减运算

既然指针变量存储的是数据的内存地址,就可将指针视为类似整型的变量。指针加上或减去一个整数的结果应该是一个新的地址值。

指针的加减运算与普遍变量的加减运算不同。一个指针加上或减去一个整数n,表示指


C++程序设计 第八章 指针和引用.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:4课堂教学中小组合作评价方法的训练

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: