指针的定义和赋值_第1页
指针的定义和赋值_第2页
指针的定义和赋值_第3页
指针的定义和赋值_第4页
指针的定义和赋值_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

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

文档简介

1、3.1指针指针的定义和赋值在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不同,如整型量占2个单元,字符量占1个单元等。为了正确地访问这些内存单元,必须为每个内存单元编上号。根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。内存单元的指针和内存单元的内容是两个不同的概念。可以用一个通俗的例子来说明它们之间的关系。我们到银行去存、取款时,银行工作人员将根据我们的帐号去找我们的存款单,找到之后在存单上写入存款、取款的金额。

2、在这里,帐号就是存单的指针,存款数是存单的内容。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。在+语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。图3.1.1中,设有字符变量C,其内容为“K”(ASCII码为十进制数75),C占用了011A号单元(地址用十六进制数表示)。设有指针变量P,内容为011A, 这种情况我们称为P指向变量C,或说P是指向变量C的指针。严格地说,一个指针是一个地址,它是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。但常把指针变量简称为指针。为了避

3、免混淆,我们在本书中约定:“指针”是指地址,是常量,“指针变量”是指取值为地址的变量。定义指针的目的是为了通过指针去访问内存单元。指针是存放地址值的变量或者常量。例如:int a=1; &a就表示指针常量(“&”表示取地址运算符,也即引用)。int *b; b表示的是指针变量(注意,是b表示指针变量而不是*b),*表示要说明的是指针变量。大家注意int *b2和int(*b)2是不同的,int *b2表示b是一个具有两个元素的数组变量,该数组中的元素为指针类型,即b是一个指针数组,而int(*b)2则表示b是一个指针,该指针指向一个含有两个int型元素的数组类型。这里要注意运算

4、优先级问题,有助于理解指针问题。下面是指针定义的例子:int *ip1,*ip2;string *pstring;double *dp;我们通过在标识符前加一个解引用操作符(*)来定义指针。在逗号分隔的标识符列表中,每个将被用作指针的标识符前都必须加上解引用操作符。在下面的例子中,lp是一个指向long型对象的指针,而lp2则是一个long型的数据对象,而不是指针:long *lp,lp2;在下面的例子中,fp是一个float型的数据对象,而fp2是一个指向float型对象的指针:float fp,*fp2;为清楚起见,最好写成:string *ps; 而不是:string* ps;有可能发生

5、的情况是,当程序员后来想定义第二个字符串指针时,他会错误的修改定义如下:string *ps,ps2; /错误:ps2不是一个字符串指针当指针持有0值时,表明它没有指向任何对象,或持有一个同类型的数据对象的地址。已知ival的定义:int ival=1024;下面的定义以及对两个指针pi和pi2的赋值都是合法的。int *pi=0; /pi被初始化为没有指向任何对象int *pi2=&ival; /pi2被初始化为ival的地址pi=pi2; /ok:pi和pi2现在都指向ivalpi2=0; /现在pi2没有指向任何对象指针不能持有非地址值。例如,下面的赋值将导致编译错误:pi=iv

6、al; /错误:pi被赋以int值ival指针不能被初始化或赋值为其它类型对象的地址值。例如,已知如下定义:double dval;double *pd=&dval;那么,下面两条语句都会引起编译时刻错误:pi=pd; /无效的类型赋值:int *<=double *pi=&dval;不能说pi在物理上不能持有与dval相关联的内存地址:它能够。但是不允许,因为,虽然pi和pd能够持有同样的地址值,但对那块内存的存储布局和内容的解释却完全不同。当然,我们要做的仅仅是持有地址值(可能是一个地址同另一个地址作比较),那么指针的实际类型就不重要了。C+提供了一种特殊的指针类型来

7、支持这种需求:空(void *)类型指针,它可以被任何数据指针类型的地址值赋值(函数指针除外)。void *pv=pi; /ok:void *可以持有任何指针类型的地址值pv=pd;void *表明相关的值是个地址,但该地址的对象类型不知道。我们不能够操作空类型指针所指向的对象,只能传送该地址值或将它与其它地址值作比较。已知一个int型指针对象pi,当我们写下pi时,表示pi当前持有的地址值。当我们写下&pi时,表示指针对象pi被存储的位置的地址。要想操作指针所指向对象的值,我们必须使用解引用操作符(*),来间接的读和写指针所指向的对象。例如:已知下列定义:int ival=1024,

8、ival2=2048;int *pi=&ival;下面给出了如何解引用pi以便间接访问ival:*pi=ival2; /解除pi的引用,为它所指向的对象的ival,赋以ival2的值/对于右边的实例,读取pi所指对象的值/对于左边的实例,则把右边的表达式赋给对象*pi=abs(*pi); /ival=abs(ival);*pi=*pi+1; /ival=ival+1;我们知道,当取一个int型对象的地址时:int *pi=&ival; 结果是int *即指向int的指针。但我们取指向int型的指针的地址时:int *ppi=&pi; 结果是int*即指向int指针的指针

9、。当我们解引用ppi时:int *pi2=*ppi;这样,我们就获得了指针ppi所持有的地址值在本例中,即pi持有的地址值,而pi又是ival的指针。为了实际访问到ival,我们需要两次解引用ppi。例如:cout<<"the value of ivaln" <<"direct value:"<<ival<<"n" <<"indirect value:"<<*ppi<<"n" <<"doub

10、ly indirect value:"<<*ppi <<endl;下面两条赋值语句的行为截然不同,但它们都是合法的。第一条语句增加了pi指向的数据对象的值,而第二条语句增加了pi包含的地址的值。int i,j,k;int *pi=&i;*pi=*pi+2; /i加2(i=i+2)pi=pi+2; /加到pi包含的地址上指针可以让它的地址值增加或减少一个整数值。这类指针操作,被称为指针的算术运算。这种操作看上去并不乐观,我们总认为是数据对象的加法,而非离散的十进制数值的加法。指针加2意味着给指针持有的地址值增加了该类型的两个对象的长度。例如,假设一个ch

11、ar是一个字节,一个int是4个字节,double是8个字节,那么指针加2是给其持有的地址值加2、8还是16,完全取决于指针的类型是char、int还是double。实际上,只有指针指向数组元素时,我们才能保证较好的运用指针的算术运算。在前面的例子中,我们不能保证三个整数变量连续存储在内存中。因此ip+2可能产生也可能不产生一个有效的地址,这取决于该位置上实际存储的是什么。指针算术运算的典型用法是遍历一个数组,例如:int ia10;int *iter=&ia0;int *iter_end=&ia10;while (iter!=iter_end) do_something_wi

12、th_value(*iter); +iter;指针变量的运算指针变量可以进行某些运算,但其运算的种类是有限的。它只能进行赋值运算和部分算术运算及关系运算。1指针运算符(1)取地址运算符&取地址运算符&是单目运算符,其结合性为自右向左,其功能是取变量的地址。前面介绍指针变量赋值中,我们已经了解并使用了&运算符。(2)取内容运算符*取内容运算符*是单目运算符,其结合性为自右向左,用来表示指针变量所指的变量。在*运算符之后跟的变量必须是指针变量。需要注意的是指针运算符*和指针变量说明中的指针说明符* 不是一回事。在指针变量说明中,“*”是类型说明符,表示其后的变量是

13、指针类型。而表达式中出现的“*”则是一个运算符,用以表示指针变量所指的变量。例如:main()int a=5,*p=&a;cout<<*p;.表示指针变量p取得了整型变量a的地址,输出语句输出变量a的值。2指针变量的运算(1)赋值运算指针变量的赋值运算有以下几种形式:指针变量初始化赋值,前面已作介绍。把一个变量的地址赋予指向相同数据类型的指针变量。例如:int a,*pa;pa=&a; /把整型变量a的地址赋予整型指针变量pa把一个指针变量的值赋予指向相同类型变量的另一个指针变量。例如:int a,*pa=&a,*pb;pb=p

14、a; /把a的地址赋予指针变量pb由于pa,pb均为指向整型变量的指针变量,因此可以相互赋值。把数组的首地址赋予指向数组的指针变量。例如: int a5,*pa;pa=a; /数组名表示数组的首地址,故可赋予指向数组的指针变量pa也可写为:pa=&a0; /数组第一个元素的地址也是整个数组的首地址,也可赋予pa当然也可采取初始化赋值的方法:int a5,*pa=a;把字符串的首地址赋予指向字符类型的指针变量。例如:char *pc; pc="C language"或用初始化赋值的方法写为

15、:char *pc="C Language"这里应说明的是并不是把整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量。在后面还将详细介绍。把函数的入口地址赋予指向函数的指针变量。例如:int (*pf)(); pf=f; /f为函数名(2)加减算术运算对于指向数组的指针变量,可以加上或减去一个整数n。设pa是指向数组a的指针变量,则pa+n,pa-n,pa+,+pa,pa-,-pa 运算都是合法的。指针变量加或减一个整数n的意义是把指针指向的当前位置(指向某数组元素)向前或向后移动n个位置。应该注意,数

16、组指针变量向前或向后移动一个位置和地址加1或减1 在概念上是不同的。因为数组可以有不同的类型, 各种类型的数组元素所占的字节长度是不同的。如指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址,而不是在原地址基础上加1。例如:int a5,*pa;pa=a; /pa指向数组a,也是指向a0pa=pa+2; /pa指向a2,即pa的值为&pa2指针变量的加减运算只能对数组指针变量进行,对指向其它类型变量的指针变量作加减运算是毫无意义的。(3)两个指针变量之间的运算只有指向同一数组的两个指针变量之间才能进行运算

17、,否则运算毫无意义。两指针变量相减两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址) 相减之差再除以该数组元素的长度(字节数)。例如pf1和pf2 是指向同一浮点数组的两个指针变量,设pf1的值为2010H,pf2的值为2000H,而浮点数组每个元素占4个字节,所以pf1-pf2的结果为(2000H-2010H)/4=4,表示pf1和 pf2之间相差4个元素。两个指针变量不能进行加法运算。例如,pf1+pf2是什么意思呢?毫无实际意义。两指针变量进行关系运算指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系

18、。例如:pf1= =pf2表示pf1和pf2指向同一数组元素pf1>pf2 表示pf1处于高地址位置pf1<pf2 表示pf1处于低地址位置例3.1 #include <iostream.h>main()int a=10,b=20,s,t,*pa,*pb;pa=&a;pb=&b;s=*pa+*pb;t=*pa*pb;cout<<"a="<<a<<"nb="<<b<<"na+b="<<a+b<<"

19、;na*b="<<a*b<<endl;cout<<"s="<<s<<"nt="<<t<<endl;.程序说明如下:说明pa,pb为整型指针变量。给指针变量pa赋值,pa指向变量a。给指针变量pb赋值,pb指向变量b。本行的意义是求a+b之和,(*pa就是a,*pb就是b)。本行是求a*b之积。输出结果。输出结果。.指针变量还可以与0比较。设p为指针变量,则p= =0表明p是空指针,它不指向任何变量;p!=0表示p不是空指针。空指针是由对指针变量赋予0值而得到的。

20、例如: #define NULL 0int *p=NULL; 对指针变量赋0值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的,否则将造成意外错误。而指针变量赋0值后,则可以使用,只是它不指向具体的变量而已。例3.2#include <iostream.h>main()int a,b,c,*pmax,*pmin;cout<<"input three numbers:"<<endl;cin>>a>>b>>c;if

21、(a>b)pmax=&a;pmin=&b;elsepmax=&b;pmin=&a;if(c>*pmax) pmax=&c;if(c<*pmin) pmin=&c;cout<<"max="<<*pmax<<endl;cout<<"min="<<*pmin<<endl;.程序说明如下:说明pmax,pmin为整型指针变量。输入提示。输入三个数字。如果第一个数字大于第二个数字指针变量赋值指针变量赋值指针变

22、量赋值指针变量赋值判断并赋值判断并赋值输出结果.指针的应用及注意的问题1理解指针的关键所在对指针类型和指针所指向的类型的理解指针类型:可以把指针名字去掉,剩下的就是这个指针,例如:int *a; /指针类型为int *int *a ;/指针类型为int *int *(*a)8; /指针类型为 int *(*)8指针所指向的类型:是指编译器将把那一片内存所看待成的类型。这里只要把指针声明语句中的指针名字和名字右边的“*”号去掉就可以了,剩下的就是指针所指向的类型。2 指针的应用传递参数实际上它可以相当于隐式的返回值,这比return方法要灵活的多,因为它可以返回更多的值,我们看下面的程序:例3.

23、3# include <iostream.h>void example(int *a1,int &b1,int c1) *a1*=3; +b1; +c1;void main() int *a; int b,c; *a=6;b=7;c=10; example(a,b,c); cout <<"*a="<<*a<<endl; cout <<"b="<<b<<endl; cout <<"c="<<c<endl;输出:*a=

24、18b=8c=10我们可以看到,*a和b的值都改变了,而c没有变。这是由于a1是指向*a(=6)的指针,也即与a是指向同一个地址,所以当a1指向的值改变了,*a的值也就改变了。在函数中的参数使用了引用(int &b1),b1是b的别名,也可以把它当作特殊的指针来理解,所以b的值会改变。函数中的参数int c1只是在函数中起作用,当函数结束时候便消失了,所以在main()中不起作用。3关于全局变量和局部变量的一个问题我们先看下面的程序:例3.4#include <iostream.h>int a=5;int *example1(int b) a+=b;return &

25、a; int *example2(int b) int c=5;b+=c;return &b;void main()int *a1=example1(10);int *b1=example2(10);cout <<"a1="<<*a1<<endl;cout <<"b1="<<*b1<endl;输出结果:a1=15 b1=4135*b1怎么会是4135,而不是15呢?是程序的问题?没错吧? 由于a是全局变量,存放在全局变量的内存区,它一直是存在的;而局部变量则是存在于函数的栈区,当函数example2()调用结束后便消失,是b指向了一个不确定的区域,产生指针悬挂。下面是对example1()和example2()的反汇编(用TC+ 3.0编译): 例3.5example1():push bp; /入栈 mov bp,sp; mov ax,bp+04; /传递参数add 00AA,ax; /相加 mov ax,00AA ; /返回了结果所在的地址pop bp; /恢复栈,出栈ret; /退出函数example2():push bp; /入栈 mov bp,sp;sub sp,02; mov word ptr bp-02,0005;mov

温馨提示

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

评论

0/150

提交评论