教学材料《C++语言程序设计》-第6章_第1页
教学材料《C++语言程序设计》-第6章_第2页
教学材料《C++语言程序设计》-第6章_第3页
教学材料《C++语言程序设计》-第6章_第4页
教学材料《C++语言程序设计》-第6章_第5页
已阅读5页,还剩47页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

6.1指针变量的定义和初始化在C++语言中,有一种特殊的变量——指针变量,简称为指针。指针是一个变量,它和普通变量一样占有一定的存储空间。但是,指针变量与普通变量不同的是指针变量的存储单元中存放的不是普通的数据,而是地址量数据。指针也是C++语言中的一个复杂的数据类型。其复杂在于它的数据类型的特殊性,应用的灵活性、多变性,而不是数据的本身。下一页返回6.1指针变量的定义和初始化6.1.1指针与指针的目标变量

设有普通变量a的一个名字为pa的指针变量,pa和a通过赋值运算“pa=&a;”使变量a的地址装入指针变量pa的存储单元,也就是指针变量pa的内容是变量a所在内存单元的地址,即pa指向了变量a。如图6−1(a)所示。

一个指针变量得到了某一地址时,我们称该指针指向了那个地址的内存区域。这样就可以通过指针对所指内存区域的数据进行处理。我们把指针指向内存区域中的数据称为指针的目标。上一页下一页返回6.1指针变量的定义和初始化若指针指向的区域是一个变量的内存单元,这个变量称为指针的目标变量。指针的目标用指针变量名字前边加上取内容运算符*表示,也称指针pa取内容。例如:指针pa的目标是*pa。如图6−1(b)所示,目标变量就是a。从图6−1可知:变量a的内容就是指针变量pa的目标,*pa即是指针变量pa的目标。变量a与*pa是等价的两种表示形式。

表达式*pa实际上是一个运算表达式,“*”是单目运算符而不是算术运算的乘法运算符;上一页返回下一页6.1指针变量的定义和初始化pa是运算分量,“*”表示访问地址中的内容。从附录A中可知,“*”运算符具有第2级的优先级。

如果将普遍变量参加运算,即其数据参加运算称为直接寻址,而指针的目标参加运算则类似于汇编语言的间接寻址。普通变量以其所在地址的内容参加运算,例如图6−1(a)中的变量a参加某一运算,不是以其本身参加,而是以a变量所在地址的内容参加运算。而指针变量的目标参加运算一般都是指针变量的内容参加运算,如图6−1(b)中的*pa,其中pa中存放的是变量a的地址,是以指针变量pa的内容参加运算,这正好与汇编语言的间接寻址相一致。下一页返回上一页6.1指针变量的定义和初始化“*”运算和“&”运算互为逆运算。例如:&(*pa),其结果是pa,也就是指针所指向目标的地址,而*(&a),表示访问变量a所在地址的内容,也就是a变量所具有的值。

如果有变量a和b,为了把变量a的值赋予变量b,有两种方法可用:b=a;如图6−2(a)所示。pa=&a;b=*pa;如图6−2(b)所示。

由图可知,使用指针是以其目标实现对内存单元的数据进行处理的。上一页下一页返回6.1指针变量的定义和初始化还有一个问题需要弄清楚。例如有下面两个赋值运算:*pa=a;和pa=&a。

从效果上看,它们都是使指针的目标*pa得到变量a的值,但在处理过程上它们有质的不同:

“*pa=a;”是把变量a的值赋给指针pa所指的地址单元,a和*pa是两个不同的存储单元,且指针pa一定是已被地址赋过值的。如图6−3(a)所示。

“pa=&a”是把变量a的地址赋给指针变量pa,使指针pa指向变量a。此时指针的目标*pa和变量a共用同一个内存单元,如图6−3(b)所示。当然*pa就是a的值,它们是一个数据的两种变量的表现形式。上一页下一页返回6.1指针变量的定义和初始化(a)*pa=a;(b)pa=&a其前者是把数据从一个存储单元复制到另一个存储单元的过程,而后者是通过地址赋值使指针指向另一个数据的过程。使用指针,要注意区分如下三种表示方法的含义。例如有一个指针pa:

pa//是指针变量,其内容是地址量

*pa//是指针的目标,其内容通常是一般的数据

&pa

//是指针变量所占存储单元的地址上一页下一页返回6.1指针变量的定义和初始化程序中还经常使用空指针的概念。我们将指针的内容为空称该指针是空指针。空指针并不是指针的存储单元为空,而是有着一个特定的空值,即是ASCII码值为0。空指针不表示任何指向,是指针的一种状态,它在程序中经常作为一种状态标志使用。而指针变量不赋值地址量时,其地址值一般是随机的,使用时可能无意入侵系统而造成系统瘫痪。

还应指出,指针变量只能接纳地址量赋值,因为表达式、常量或寄存器变量没有地址,所以它们不能向指针变量赋值。

指针除了可以指向变量外,还可以指向数组、结构体、联合体,甚至可以是函数,即凡是有存储地址的都可以用指针指向它。上一页下一页返回6.1指针变量的定义和初始化6.1.2指针变量的定义与初始化

由于指针是变量,所以程序中使用指针变量之前,必须在变量的定义部分预先进行定义。指针变量定义的格式为:

[存储属性]数据类型*标识符

其中,标识符就是指针变量名,存储属性是指针变量本身的存储属性,它与普通变量一样,可分为auto型,static型和外部(extern)型。不同存储属性的指针所占用的存储区域不同。指针的存储属性和指针定义时所在程序中的物理位置决定了指针的存在性和可见性,即指针也可以分为内部的和外部的、全局的和局部的。上一页下一页返回6.1指针变量的定义和初始化指针定义时的数据类型不是指针本身的数据类型,而是指针所指目标的数据类型。指针变量所保持的内容总是数值化了的地址量,没有其他类型,不要将指针定义时的数据类型误认为是指针变量的数据类型。例如:

int*pa;char*name;staticint*pb;

其中,pa是指向整型数据的指针,name是指向char型数据的指针;pb是指向整型数据的静态指针。和人们简称指针变量为指针一样,人们通常又把指向××类型的指针就简称为××类型指针,但绝不能混淆其具体含义。上一页下一页返回6.1指针变量的定义和初始化和普通变量一样,也可以将同一存储属性和数据类型的指针在一行中定义。例如:

int*pa,*pb,*pc;chara,*name;

指针也可以进行初始化。其格式为:

[存储属性]数据类型*标识符=地址量;

例如:inta,*pa=&a;

上述语句是把变量a的地址赋予指向整型的指针pa,而绝不是将一个初始化的地址量赋予指针的目标。上一页返回6.1指针变量的定义和初始化指针定义或初始化时需注意:

(1)在指针定义或初始化时标识符前面的“*”,表示该变量是个指针变量,以便于与其他变量相区别,不是乘法运算符,也不是取内容运算符。例如:

inta,*pa=&a;

它是对指针进行初始化,此处pa是指针,*pa并不是指针所指的目标。

(2)把一个变量的地址作为初始化值赋给指针时,该变量必须是在指针初始化之前已经定义了的。道理很简单,变量只有在定义之后才被分配一定的内存单元,才具有确定的地址。

下一页返回6.1指针变量的定义和初始化(3)指针定义时的数据类型必须与指针所指的目标的数据类型相一致。这是因为数据类型不同所占用内存单元的字节数也不同,在做指针的增1、减1及其移动操作时跳过的字节数也不同,否则会造成错误的操作。(4)可以用初始化了的指针给另一个指针做初始化赋值。

(5)可以将一个指针初始化为空指针。C++语言中有许多与指针操作有关的库函数,在操作不成功时返回的就是空指针。这里的零不是数值0,而是ASCII码空的代码值。

上一页下一页返回6.1指针变量的定义和初始化(6)不能用一个内部auto型变量的地址去初始化一个外部或static型的指针,例如:

{intl;staticint*pl=&l;

…}这是因为内部auto型变量每次流程进入该函数或分程序时都被重新分配内存单元,退出后内存单元即被释放。而静态型指针却要长期占用已分配的内存单元,当程序流程退出后,内存单元也不释放。这样会使静态指针指向的是一个可能已经被释放了的内存单元。上一页下一页返回6.1指针变量的定义和初始化6.1.3近程和远程指针

指针也有远、近之分,当指针所指向的目标与指针在同一个段时,它们的段地址相同,这时用段内指针来指向目标,此时的指针是近指针,其指针修饰符是near,这类指针占用两个字节(16位),指针只存储所指目标的段内偏移地址。近指针操作速度快且节省内存。

当指针与所指向的目标不在同一个段时,例如large模式下数据与代码是跨段的,这时仅仅保存段内偏移量是不够的,还要保存段地址,这类指针占用4个字节(32位),其指针的修饰符是far。

指针所指内容的引用就是将指针名前加上“*”,取内容运算符构成指针的目标。上一页返回6.2指针运算6.2.1指针的一般运算

1.取地址运算(&)

单目运算符“&”的功能是取操作对象的地址。例如:&x是取变量x在内存单元的地址。“&”的操作对象只能是变量或数组元素而不能是常量或表达式。2.赋值运算(=)

指针赋值运算通常是将某个变量的地址赋给指针变量,以使指针变量指向该变量。3.取内容运算(*)

单目运算符“*”是取地址表达式所指向地址的内容。它与“&”互为逆运算。下一页返回6.2指针运算6.2.2指针的算术运算

1.指针与整数的加、减运算

指针变量加上或减去一个整数n,其意义是把指针由当前所指向的位置向前或向后移动n个数据的位置。我们所说的n个数据位置的含义是:如果指针pa指向的是整型数据,加n时,实际操作是将指针向前移动4n个字;若pa指向的是float型数据时,减去n,实际操作是将指针向后移动4n个字节。对于指向不同的数据类型的指针pa,pa±n表示实际移动的字节数是:n×该指针所指向数据类型所占字节数。pa±n的实际地址是:(pa)±n×该指针所指向的数据类型所占字节数。(pa)表示指针pa当前的地址量。下一页返回上一页6.2指针运算2.指针加1、减1运算

指针加1、减1单目运算也是地址运算。它具有上述的运算特点,只是其所指向的地址不是(pa)±n×数据类型所占字节数,而是(pa)±数据类型所占字节数。指针加1运算后就指向了下个数据的起始位置;指针减1运算就指向上一个数据的起始位置。

指针加1、减1单目运算也分前置和后置运算。当它们与其他运算符组成一个表达式时,要特别注意它们之间的优先顺序和结合性。上一页下一页返回6.2指针运算3.指针相减指针变量相减的充要条件是:两个指针不但要指向同一数据类型的目标,而且要求所指对象是唯一的。设有指针x和y是指向同一组数据类型一致的数据,x−y运算的结果是两指针所指向数据的地址之间数据的个数。由此得知,指针相减运算不是两指针所持有地址值的单纯相减,而是按如下公式得出其操作结果:

(x)−(y)

一个数据占用字节数

式中(x)和(y)分别表示指针x和y的地址值。两指针相减后的结果值是两指针所指向地址之间数据的个数。上一页下一页返回6.2指针运算6.2.3指针的关系运算

同指针相减运算类似,两个指向同一组数据类型的数据的指针,也可以进行各种关系运算。两指针之间的关系运算表示它们指向的地址位置之间的关系。例如:两个指针x和y间的关系若是x<y,如果x指针指向的位置在y指针指向的位置的前方,即x指针所指向的地址量小于指针y所指向的地址量,则该表达式值为真,反之则为假。两指针相等的概念是两个指针指向同一位置。

不是指向同一组数据类型的数据的两个指针之间不能进行关系运算。指针不能与一般数值进行关系运算,但指针可以和零进行等于或不等于的关系运算。即

pa==0或pa!=0这是用于判断指针pa是否是个空指针的关系运算。上一页下一页返回6.2指针运算由于while()循环语句中的循环控制表达式是*p!='\0',而“\0”的实际值就是零值。因此可以将其循环控制条件表达式简化为*p,这样while语句循环控制表达式可以写成:while(*p)。

实际上,可将检测字符串长度的功能函数段写成为一个功能函数:

intslength(char*s){char*p=s;while(*p)p++;return(p−s);}函数slength中的返值表达式是两个指针相减p−s,其差就是被检测的字符串的长度。上一页下一页返回6.2指针运算如果两个字符串相等是指两个字符串的字符个数相等,且每个对应字符都一样。如果s所指向的字符串小于t所指向的字符串,要么是s所指的字符串的长度小于t所指向的字符串的长度,而先到达其字符串结束标志“\0”,要么是s所指向的字符串的对应字符的ASCII代码值小于t所指向字符串的对应字符的ASCII代码值,此时函数返值为负的,否则,返值是正的。

应该特别指出,语句return(*s−*t);不是指针相减,而是指针所指目标的ASCII代码值相减。上述函数strcomp上机调试时要加上main()函数,在main()函数中调用strcomp()函数,以检验其正确性。strcmp()是C++语言中的一个字符串比较的库函数。上一页下一页返回6.2指针运算指针运算规则很重要,它是指针运算必须遵循的原则。要澄清如下两个错误的认识:

(1)认为指针与整型变量是一样的,这就混淆了指针变量与整型变量的本质区别。

(2)当两个指针指向的目标的数据类型相同时,就认为可以对两个指针进行相减运算或关系运算,那也是不确切的。因为C++语言规定,两指针进行相减或关系运算的充要条件是:两个指针不但要指向同一数据类型的目标,同时还要求该目标是唯一的。上一页返回6.3指针与数组、指针数组

6.3.1指针与数组

在C++语言中,数组和指针是密切相关的,而使用指针对数组元素的存取操作将会比用下标对数组元素的存取操作更方便,速度更快。如果有语句:

intarray[8],*parray;

它定义了一个长度是8个元素的整型数组,其数组元素分别是array[0],array[1],…,array[6],array[7]。它们在内存中是一个“连续”的存储区间。array[i]表示距离数组array[]的首地址为i的数组元素,也就是说array[i]是数组array[]的第i个元素。当使用下列地址赋值后:parray=&array[0]或parray=array将使指针与数组建立起联系,即指针parray指向了数组array[]的第一个元素的地址,如图6−4所示。下一页返回6.3指针与数组、指针数组

在这种情况下,可以使用指针加减一个整数使指针指向对应的元素。

图6−4右侧的是指针变量与数组元素的对应表示。由此可见,当指针变量parray指向array[i]时,下面的表示是等价的。

*(parray)//表示array[i]的内容

*(parray+1)//表示array[i+1]的内容

*(parray−1)//表示array[i−1]的内容

从图6−4还可以看出,指针变量parray加上或减去整数i,通过i的变化可以和数组的下标处理方法一样来处理存放在内存中“连续”区域的数组的各元素。上一页下一页返回6.3指针与数组、指针数组

6.3.2指针数组

一系列有序的指针集合构成为指针数组。指针数组的每个元素都是一个指针变量,它们具有相同的存储类型和指向相同的数据类型。和普通数组一样,在使用数组之前必须通过数组定义语句对其进行定义。其一般形式是:[存储属性]数据类型*数组名[常量表达式][常量表达式]…指针数组和普通数组的差别是,指针数组的元素是指针,在数组定义和引用时,数组名前应加上“*”以便与其他数组相区别。和普通数组一样,编译系统在编译指针数组定义时,按其指定的存储属性为之分配相应的存储空间。此时,数组名就是指针数组在内存存储区域的首地址。上一页返回6.4指向数组的指针与多级指针6.4.1指向数组的指针

若有定义:

inta[3][2],(*pra)[2];说明符(*pra)[2]中,由于一对圆括号的存在,“*”要首先与pra结合,说明pra是一个指向数组的指针,然后再与说明符[2]结合,说明指针变量pra是指向含有两个int型元素的下一页返回6.4指向数组的指针与多级指针一维数组,这种指针又称为行指针。“pra=a;”是使指向数组的指针pra指向数组a。pra+1等价于a+1;可以通过以下形式应用a数组元素a[i][j]:

*(pra[i]+j)等价于*(a[i]+j)*(*(pra+i)+j)等价于*(*(a+i)+j)(*(pra+i))[j]等价于(*(a+i))[j]pra[i][j]等价于a[i][j]

在此,pra是指向数组的指针变量,其值是可变的,而a是地址常量,其值不能变化。上一页返回下一页6.4指向数组的指针与多级指针注意:

(1)当二维数组名作为实参时,对应的形参必须是行指针变量。若有:

#defineM5#defineN3main(){ints[M][N];…;fun(s);…}函数fun()的形参可以是以下三种形式之一:

fun(int(*a)[N])或fun(inta[][N])或fun(inta[M][N])下一页返回上一页6.4指向数组的指针与多级指针(2)当指针数组作为实参时,对应的形参应是指向指针的指针,也就是二级指针。若有:

#defineM5#defineN3main(){ints[M][N],*ps[M],k;…;for(k=0;k<M;k++)ps[k]=s[k];fun(s);…}上一页下一页返回6.4指向数组的指针与多级指针6.4.2多级指针

在C++语言中,数组中的数据可以使用相应的指针进行“处理”。我们可以扩展为指针数组中的“数据”也可以用指针进行相应的处理,即指针数组也可以用另一个指针进行处理。我们把指向指针的指针称为二级指针,二级及其以上的指针都称为多级指针。对于二级指针的定义与指针定义的差别是在指针名前多加一个“*”号。若是n级指针,就是在定义指针时在指针名的前面加n个“*”号。上一页下一页返回6.4指向数组的指针与多级指针例如:有一个字符型指针数组是:“char*str[3];”,它的三个元素str[0],str[1],str[2]都是指针,它们分别指向一个字符串,如图6−6所示,三个字符串的首字符分别由*str[0],*str[1],*str[2]表示。如果同时有另一个指针ps,且把指针数组str的首地址赋给指针ps:ps=str或ps=&str[0]则ps指针就指向指针数组str[],因此ps的目标*ps就是str[0],*(ps+1)就是str[1],*(ps+2)就是str[2]。上一页下一页返回6.4指向数组的指针与多级指针在图6−6中,指针str[0的目标就是*str[0],即指向字符h。如果用二级指针表示,str[0]是*ps,所以*str[0]就是**ps。二级指针ps指向指针*ps,它是一级指针,*ps指向被处理的数据是**ps。同理,**(ps+1)和**(ps+2)分别指向另外两个字符串的首字符。如果还存在一个指向ps的指针,则该指针就是三级指针。

多级指针的定义和一级指针的定义类似,存储属性是指指针本身的存储属性,而数据类型则是最低层目标数据类型。上一页返回6.5指针与函数6.5.1作为函数参数的指针

我们在介绍函数时介绍了C++语言的函数间传递数据有传值方式、传址方式以及利用全局变量和return语句传递处理结果的各种方式。已经讲述过,函数间采用传值方式传递数据时,被调函数并不能改变主调函数中变量的内容。而使用传址方式却可以达到上述目的。返回下一页6.5指针与函数函数是:

voidswap(inta,intb){inttemp;

130C++语言程序设计

cout<<"刚进入swap(a,b)函数中的a和b的值是:\n";

cout<<"a="<<a<<"b="<<b<<endl;

cout<<"进行交换之后的a和b的值是:\n";

temp=a;a=b;b=temp;

cout<<"a="<<a<<"b="<<b<<endl}返回上一页下一页6.5指针与函数当主调函数需要进行两个元素交换时,调用swap()函数并未达到使x,y交换其值的目的。原因是用传值方式传递数据,形式参数和实在参数各自占用不同的存储单元,而形式参数间内容的交换并没有影响到主调函数中实在参数的内容。如图6−7(a)所示。函数;swap()函数的形式参数是指针变量a和b,指针变量a和b接收x和y的内存地址值,使指针变量a和b指向主调函数中实在参数的内存单元,这样,形式参数和实在参数不是相互独立的两个存储单元,而是只有实在参数的单元和指向它们的指针。所以,在swap()函数中所进行的交换操作,实际上是通过指针变量a和b直接对主调函数中的变量进行交换操作。其操作过程如图6−7(b)所示。下一页返回上一页6.5指针与函数需要指出:一个函数中用于接收实在参数中地址量的形式参数必须是指针类型。上述交换操作后,实在参数中的内容确实得到了交换。作为函数参数的指针其存储属性只能具有局部的属性,即其存在性和可见性仅限于包含它们的这个功能函数。

指针作为函数的形式参数经常用于主调函数需要得到多个处理结果的情况下,例如上述的交换以及对数组的处理等方面。上一页下一页返回6.5指针与函数6.5.2指针型函数

我们从函数参数的角度讨论了函数与变量,数组,字符串的关系;我们又讨论了指针为函数参数的问题。本小节将从函数返值方面讨论函数的有关性质。前已叙及,函数返值的数据类型决定了函数的数据类型。迄今为止,带返值的函数都返回一定数据类型的数值。当函数的返值是地址时,我们称这类函数是指针型函数。也有资料称为返回地址的函数。返回上一页下一页6.5指针与函数指针型函数定义的形式是:

[存储属性]数据类型*函数名(形参表){函数体}指针型函数的存储属性是该函数本身的存储属性。它和一般函数一样可分为外部型和静

态型。数据类型是返值地址所在内存单元存放数据的数据类型。这是与一般函数不同的一点,需特别注意。指针型函数与一般型函数的外在区别是定义时函数名前加一个“*”号;其内在区别是函数的返值是地址量。由于指针型函数的返值是地址量,所以用于接收其地址的变量必须是和该指针型函数的数据类型一致的指针。指针型函数在调用之前都需要进行说明。函数说明的形式是:

[存储属性]数据类型*指针型函数名();返回上一页下一页6.5指针与函数6.5.3指向函数的指针

在C++语言中,函数的定义是不能嵌套的,即不能在一个函数的定义中再对其他函数进行定义。

C++语言中的数组名表示该数组在内存存储区域的首地址,可以把数组名赋予具有相同数据类型的指针变量,使指针指向该数组。函数的函数名也具有上述数组名的特性,也就是说,函数名则表示该函数在存储区域的首地址,即是函数执行的入口地址。在程序中调用函数时,程序控制流程转移到的位置就是函数名给定的入口地址。

把一个函数名赋予指针变量时,指针变量的内容就是该函数在内存存储区域的首地址。我们把这种指针称为指向函数的指针,简称函数指针。上一页下一页返回6.5指针与函数指向函数的指针定义的一般形式是:

[存储属性]数据类型(*函数指针名)();

其存储属性是函数指针本身的存储属性,数据类型则是函数指针所指向的函数所具有的

返值类型。

函数指针和其他指针的性质基本相同,例如:在程序中不能使用不定向的函数指针,函数指针被赋予某个函数名时,该函数指针就指向赋予函数名的那个函数。

但也有一些不同之处,例如:对于有一定指向的函数指针进行取内容“*”运算时,其结果是将程序的控制流程转移到函数指针所指向的地址执行该函数体,这点与其他指针不同。上一页下一页返回6.5指针与函数需要特别注意的是:函数指针说明中的包围函数指针名的圆括号是不能省略的,如果省略了,将和指针型函数产生混淆。

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论