《C语言程序设计案例教程第7章 指针.ppt》由会员分享,可在线阅读,更多相关《C语言程序设计案例教程第7章 指针.ppt(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、C 语言程序设计案例教程第7 章 指针第7章指针指针是C 语言中一类非常重要的数据类型,是C 语言中的重要概念。指针的应用为函数间各类型数据(特别是复杂类型的数据)的传递提供了简洁的方法,可以提高程序的执行效率。另外,有些其他数据类型无法或很难实现的操作,都可以利用指针来完成。指针的概念比较复杂,使用也灵活,难于掌握。要学好C 语言,必须很好地学习指针。7.1指针的定义与应用7.1.1指针概述在计算机内,数据均存储在存储器内,存放一个字节数据的存储器叫一个存储单元。为了便于管理,系统按顺序为每个存储单元进行编号,每个存储单元都具有自己的唯一编号,这个编号就是该存储单元在内存中的地址。当在程序中
2、定义一个变量时,系统就在内存中为该变量分配一个大小合适的存储空间,所需要的空间大小由变量的类型而定,例如,一个整型数组n5 占有25 个字节。在变量定义完成后,变量就在内存中具有了一个唯一的存储空间,变量都存储在有确定地址的存储单元中,变量在内存中具有唯一的地址。对变量的访问,实质就是对变量存储空间的内容进行访问。由于各种变量在内存中所占的存储空间大小不一样,为了便于对变量地址的访问,将变量存储单元的起始地址定义为变量的地址。这样,就可以用另一种方法来访问变量内容,即不通过变量名,而是通过对变量在内存中的地址进行访问。因为在C 语言中存储单元的地址被称为指针,因此这种访问就是指针访问。变量存储
3、空间、变量地址与变量内容的关系就好像是信箱、信箱号码与信件,可以将内存单元看成一个个信箱,存储单元的地址就是信箱的号码,在定义变量时意思就是“将某号码信箱分配给某人使用”,而变量的内容就相当于是信箱内的信件,在给变量进行赋值时相当于“将某某信件放到某号码信箱”。指针就是存储单元的地址,是一个常量,将它保存在一个特殊的变量中,这个变量就叫做指针变量。为了叙述方便,常将指针变量简称为指针。在C 语言中,通常可以直接通过变量名对变量内容进行访问,而无需知道变量在内存中的地址,系统会自动将访问转换为对内存单元的访问,这种通过变量名来对变量存储单元进和访问的方式称为“直接访问”另外,还支持对变量的内存单
4、元进行“间接访问”这种访。问允许将变量的地址赋给另一个特殊的变量,程序中可以利用这个特殊的变量来访问变量的内存单元,这个特殊变量称为指针。在图711 中给出了使用指针进行“间接访问”的图示。图711变量的间接访问其中,变量n是一个int 型变量,它占有两个存储单元,变量n的地址为这两个存储单元的首地址65524,量n的值为100。针变量point 是一个指针,本身存储在地址为1000变指它172的存储单元中,指针变量point 的内容为变量n的地址65524。在这种情况下要对变量n进行访问,可以从指针变量point 中获得变量n的地址65524,再通过该地址65524进行访问。不同数据类型的变
5、量占用的存储单元个数是不一样的,指针point 正是依据它的数据类型来确定变量n占有多少个存储单元。例如,变量n是一个int 型变量,因此可以将point 定义为一个int 型指针,这样,在使用指针point 访问变量n时,程序就知道所要访问的是一个int 型变量,int 型变量占有两个存储单元,因此将对地址从65524开始的两个单元(地址分别为65524和65525)进行访问。7.1.2指针的定义指针变量是一种特殊的变量,用来专门存放变量的地址。指针变量的定义格式如下。【格式1】存储类型类型说明符*变量名1【格式2】存储类型类型说明符*变量名1,*变量名2【功能】格式1用来定义单个指针变量,
6、格式2用来定义多个同类型指针变量。定义指针时应注意以下几点。(1)存储类型”用来说明“变量名”指示的变量的数据存储类型。“(2)类型说明符”用来说明“变量名”指示的变量的数据类型;“(3)每个指针变量前都要加“*”;(4)指针是用来存储对象内存地址的变量,对象可以是简单类型数据(int,char 等),也可以是数组,函数,还可以是另一个指针;(5)指针变量的值只能是内存中存在的一个地址,而不是一个任意的整数。(6)不同类型的指针变量不能互相赋值;(7)不能够将指针值赋给整型变量或无符号整型变量;(8)void*p 表示指针p为无类型指针,即指针p可以用任意类型的指针方式来引用。【例1】下面定义
7、了point1 和point2 两个指针变量,int 表示指针point1、point2 所指的变量是一个整型变量。int*point1,*point2【例2】下面是几种常见的指针变量定义。int*n1/*n1 是一个整型指针变量*/float*fn1/*fn1 是一个浮点型指针变量*/char*sc1/*sc1 是一个字符型指针变量*/char(*sp)5/*sp 是字符数组指针变量,指向一个字符数组sp,它有5个元素*/int*ppn1/*ppn1 是一个指针变量,它是指向一个整型指针的指针变量*/int(*nf1)()/*nf1 是函数指针变量,它指向一个函数,该函数返回一个整型值*/i
8、nt*(*nf2)()/*nf2 是函数指针变量,它指向一个函数,该函数返回指向一个整数的指针*/【例3】下面定义的不是一个指针变量。char*sc15一个字符串的指针*/int*pf()/*sc1 是一个有5个元素的一维字符数组,每个字符数组元素是指向/*pf 是一个函数,这个函数返回指向一个整型值的指针*/7.1.3指针变量的引用在对指针变量的引用中,经常会进行指针变量赋地址数和利用指针间接访问变量。对指173针变量的引用,是由取地址运算符“&”和取值运算符“*”来完成的。1 取地址运算符&取地址运算符“&”在第2章第3节介绍scanf()函数时介绍过。在变量的前面添加取地址运算符“&”可
9、以获得该变量的地址。,【例1】在scanf()函数使用中的“&”运算符是将数据存储到指定的存储空间。例如:intnscanf(“%d”,&n)该程序是以格式符“%d”将输入的数据指定为整型数据,存储到整型变量n地址所指示的存储区域内的存储单元中。【例2】给指针变量所赋的值一定是地址值,可以用“&”加变量名来获取地址,也可以用数组名获得地址(因为数组名表示数组的首地址),还可以用已经保存有变量地址的指针变量获得地址。取地址运算符“&”用来将变量的地址赋给指针变量的示例如下。charSC1=Ainta=189,b=666,m=1floatn=6.18char*point1/*定义字符型指针poin
10、t1*/int*a1p,*bp1/*定义整型指针a1p 和bp1*/float*fnp1/*定义实型单精度指针fnp1*/point1=&SC1/*将变量SC1 的地址赋给指针point1*/ap1=&a/*将变量a 的地址赋给指针ap*/bp1=&b/*将变量b的地址赋给指针bp*/fnp1=&n/*将变量n的地址赋给指针fnp1*/注意:在为指针赋地址时,指针的类型应该与所指地址的变量数据类型一致。取地址运算符&只能用于变量或数组元素,而不能用于表达式或常量。例如,下面是错误的。int*point1,a,n10point1=&(a+10)point1=&1232 取内容运算符“*”在对指针
11、赋予地址值后,在地址指针的前面添加取内容运算符“*”可以获得该指针指,向的变量的值,实现对变量的简介访问。【例3】对于上面定义的指针,可以进行如下的变量访问。该程序执行后,变量SC1 的内容改变为B,变量n的值改为1.2345,变量m 的值改为855。*point1=B*fnp1=1.2345m=*ap1+*bp1/*相当于SC1=B*/*相当于n=1.2345*/*相当于m=a+b*/【例4】下面程序用来将指针point1 指向变量n的值100赋给变量m,将m+1 的值赋给指针point1 指向变量n。intn,m,*point1/*定义两个变量n和m,定义一个指针point1*/n=100
12、/*变量n初始化*/point1=&n/*指针初始化,即将变量n的地址赋给指point1 针,定向变量n*/m=*point1/*将指针point1 指向变量n的值赋给变量m,它与“m=n”语句等效*/*point1=m+1/*将m+1 的值赋给指针point1 指向变量n,它与“n=m+1”语句等效*/174注意:*point=&m;语句是错误的。“”【例5】给指针point1 赋一个“空”值,指针没有指向一个确定的地址。int*point1point1=NULL/*定义一个指针point1*/*给指针point1 赋一个“空”值*/NULL 在头文件“stdio.h”中定义,其值相当于0,
13、当执行“point1=NULL”语句后,相当于“point1=0;或“point1=0”此时指针并不指向地址为0的内存单元,而是指向空”,值。3 使用指针的注意事项(1)对指针变量进行访问之前,定要先为其赋予地址,则将发生不可预料的错误。在一否下面所示语句是错误的使用方法。int*point1*point1=88如果执行这段程序,将引起程序崩溃,因为没有对指针point1 赋内存地址值,在执行语句“*point1=88”时,*p”指向不可知的内存地址,而对不可知的内存地址进行赋值,必“将导致程序出错。(2)除NULL 外,指针必须指向内存中实际存在的地址,不可以在程序中用非地址表达式给指针变量
14、赋值。下面所示语句是错误的。int*point1,a=1point1=a+5/*不能用非地址表达式来为指针赋值*/point1=2000/*不能用常量为指针赋值*/*point1=2000/*此时可能会导致系统出错*/上面所示的两种方法都将使指针指向错误的地址,在使用语句“*point1=2000”改变指针所指地址内容时,将产生不可预知的错误。7.2指针与数组指针变量除了可以对其引用地址的内容进行运算外,其本身也可进行运算。指针是内存单元的地址,对指针本身的运算会使指针指示的存储单元发生改变。由于指针运算的结果也是一个地址,为了使该地址是一个合法地址,在指针进行运算之前,必须先将指针指向一个数
15、组,指针运算通常都是针对数组而进行的。7.2.1指针的运算当指针指向数组时,指针可以进行算术运算、关系运算和赋值运算。1 指针的算术运算设ap1 和bp1是相同类型的的指针变量,都指向同一个一维数组,变量n是整型变量,则指针可以进行如下算术运算,前四个计算结果是指针指向该数组的下一个元素的地址。(1)指针加一个整数:指针地址数加一个整数,例如,ap1+n。(2)指针减一个整数:指针地址数减一个整数,例如,ap1n。(3)指针自增(+)指针地址数自动加一个正整数,例如,ap1+、+ap1。:(4)指针自减()指针地址数自动减一个正整数,例如,ap1、ap1。:(5)两个指针相减:计算结果为两个指
16、针地址数的差,即两个指针所指地址之间的数据175个数,对应数组的元素个数,不是地址数。例如,ap1bp1。注意:指针的算术运算不允许进行乘、除一个整数的运算,不允许两个指针之间进行加法运算,也不允许指针进行加、减实型数的运算。指针自增和指针自减运算不是指指针地址数自动加1和减1,而是加或减一个正整数,具体这个正整数是多少,是由指针指向数组变量的类型来确定。如果是整型数组,则一个元素占2个字节,则加或减的正整数为2。由上面的说明可以知道,指针的运算实际上是一种地址的运算,是一种类似于对数组下标的运算。例如:设数组SL1 在内存中的首地址为65524,数组SL1 在内存中位置如图721 所示。执行
17、下面语句后会有什么变化?下面各语句的作用如下。图721 数组SL1 与指针int*sp1,*sp2,nintSL1=1,2,3,4,5,6,7,8,9,10sp1=sp2=SL1/*指针sp1 和sp2 指向数组SL1 首元素SL10 地址*/sp1+/*sp1 指向数组SL1 中下一个元素,即SL11*/n=*sp1/*将sp1 所指元素内数值赋给n,此时n=2*/n=*(sp1+6)/*将sp1 后第2个元素内数值赋给n,此时n=8,但sp1 本身未改变*/n=*sp1+10/*将sp1 所指元素SL11 内容加10后赋给n,即n=SL11+10=12*/n=sp1sp2/*将sp1 与s
18、p2 所指数组元素的下标相减,得两元素间的下标差值1*/求(1)执行“sp1=sp2=SL1”语句后,指针sp1 和sp2 都指向数组SL1 的首元素地址,值均为65524,相当于sp1=sp2=&SL10。(2)执行“sp1+”语句后,指针将指向内存地址为65526的存储单元,其实质为指向数组元素SL11 所在内存地址。(3)执行“n=*sp1”语句后,将sp1 所指元素SL11 的内容赋给n,此时n值为2。(4)sp1+6 等于SL11+6(即a7)元素的地址,n=*(sp1+6)”语句等同于取SL17“的内容赋给变量n,变量n等于8。(5)*sp1+10 等同于用sp1 所指元素值加上2
19、即SL11+2,所以语句“a=*sp1+2”等于a=k1+10 即a=12。注意:*sp1+2 和*(sp1+2)两者是完全不同的,前者是取指针所指内容再加2,后者是取指针后面第2个元素的值。(6)执行“n=sp1sp2”语句后,将指针sp1 值减去指针sp2 值,其实质是将指针sp1与sp2 所指数组元素的下标相减,求得两元素间的下标差值,由于此时指针sp1 指向数组SL11,指针sp2 指向数组SL10,则n=sp1sp2=1。2 指针的关系运算两个指针之间的关系运算表示它们所指向的地址位置关系,实质上是比较两个指针所指数组元素下标的大小关系。如果指针sp1 和sp2 指向同一个数组内的不
20、同元素,且指针sp1指向的元素下标小于指针sp2 指向的元素下标,则表关系达式“sp1sp2”的值为0(假),176否则为1(真)如果指针sp1 和sp2 指向用一个元素,则“sp1=sp2”的值为1(真)否则。,为0(假)。3 指针的赋值运算指针的赋值运算要求必须用地址数值进行赋值。例如,进行下面的定义后,可以给指针赋给不同的地址:intn,SL16int*sp1,*sp2(1)sp1=&n(2)sp2=SL1(4)sp1=sp2/*将变量n的地址赋给指针变量sp1*/*将数组SL1 的首地址赋给指针变量sp2*/*将数组元素SL15 的地址赋给指针变量sp1*/(3)sp1=&SL15/*
21、将指针变量sp2 的地址值赋给指针变量sp1*/注意:可以对一个常量或表达式进行取地址运算,赋给指针变量。如,sp1=&200”不再例“和“sp1=&(n+1)”。7.2.2数组指针和字符指针1 数组指针从上面的内容得知,指针可以用来对数组进行访问。数组的指针是指数组在内存中保存的存储单元起始地址,数据元素的指针是指数组元素在内存中的保存的存储单元起始地址。数组名也可以理解为一个指针,不过数组名是一个指针常量,不能改变。可以用数组名来将数组的首地址赋给指针。数组指针的定义与普通指针的定义方法相同,数组指针的引用,即可以用指针方式来进行,又可以用数组下标方式来进行,如果指针sp1 指向数组SL1
22、 首元素的指针,则*sp1+n)(等价于SL1n,如果指针sp1 是指向数组元素SL1k 的指针,则*(sp1+n)等价于SL1k+n。2 字符指针字符指针可以指向字符串,也可以指向字符数组。字符指针的定义、赋值和引用与数组指针基本一样。(1)字符指针指向字符串:字符串在内存中的存储与数组相类似,也是存储在一块连续的存储空间中,系统会自动在字符串结尾加上0 表示结束,这样,如果知道了字符串的首地址,只要将指针指向字符串的起始位置,就可以通过指针进行字符串的处理了。可以定义一个字符型指针,使该指针指向字符串起始地址,就可以使用该指针来进行字符串的引用,这个指针称为字符串的指针。定义字符指针的方式
23、如下。【格式1】char例如:char*SC1char*SC2=Thisisabook!char*SC3=Hello!上面的语句定义了字符指针SC1、SC2 和SC3,后两个字符指针分别指向字符串“Thisisabook!”和“Hello!”在内存中存储单元的起始地址。字符串常量的内容由系统自动为其分配存储空间,并在字符串尾加上结束符号0,字符串指针变量仅保存字符串常量的首地址。177*字符指针变量名=字符串常量除了在定义时对指针进行初始化赋值外,也可以在程序中进行赋值。定义了字符指针后,就可以在程序中对其进行引用。使用字符指针进行字符串的引用时,即可以单个引用字符,也可以整体引用一个字符串。7.2.3指针数组可以将一个数组定义为指针类型,称为指针数组。指针数组定义的格式如下。【格式】数据类型*数组名 下标char*SC15上面的语句定义了一个有5个元素的字符指针数组,组的每一个元素是一个字符指针,数可以象单个字符指针一样引用数组中的每一个字符指针。针数组常用于多个字符串的处理。指178