资源描述
第8章指针C语言程序设计与数据结构教学提示:指针是C语言的精华部分,通过利用指针可以描述复杂 的数据结构,很方便地使用数组和字符串,并象汇编语言 一样处理内存地址,使程序精练、高效。另一方面,不正 确的指针使用是危险的,轻则影响旬结果的正确性,重则 会导致系统瘫痪。对初学者来说除了要正确理解有关概念 夕卜还必须要多编程多上机调试。教学要求:本章要求掌握指针变量的定义,理解地址就是指针的的 概念,一维数组的指针与指向一维数组的指针的区别,指 针与字符串、函数的关系,指针变量作函数参数时参数的 传递方式,返回指针值的函数定义形式以及指向函数的指 针变量定义。c语言程序设计与数据结构8.1地址与指针概述 W如果我们在前面的编程中定义或说明了变量,编 译系统就为已定义的变量分配相应的内存单元(一 般把存储器中的一个字节称为一个内存单元),为 了能正确地访问这些内存单元,可以为每个内存单 元编上号,根据一个内存单元的编号就可准确地找 到该内存单元。内存单元的编号也叫做地址,通常 也把这个地址称为指针。也就是说,每个变量在内 存中会有固定的位置,即具体的地址。变量的数据 类型不同,它所占的内存单元的数目也不相同。C语言程序设计与数据结构若我们在程序中已有如下定义:int a=l,b=2;float x=3.4,y=4.5;double m=3.124;char chl=Ta ch2=b;那么编译系统可以按下面方法为变量分配内存。变量a,b 是整型变量,在内存中各占2个字节;x,y是实型,各占4个 字节;m是双精度实型,占8个字节;chi,ch2是字符型,各占1个字节。由于计算机内存是按字节编址的,设变量的 存放从内存2000单元开始存放,则编译系统对上述变量在 内存中的一种可能存放情况如图8.1所示。变量a在内存的 地址是2000,占据两个字节2000和2001后,变量b的内存地 址就为2002。以此类推,变量m的内存地址为2012等。另外 内存地址都是整形数,不会出现小数。C语言程序设计与数据结构2000200220042008201220202021123.44.53.124b图8.1不同数据类型变量在内存中占用的空间变量a 变量b变量x变量y变量m变量chi 变量ch2C语言程序设计与数据结构指针变量存放变量地址的变量称为指针变量。如果将变 量a的地址保存在内存的特定区域,用变量pa来存 放这些地址,这样的变量pa就是指针变量。因此,一个指针变量(如pa)的值就是某个变量(如a)的地址(也称为某变量的指针),也称为pa指向变 量a,或者说pa是指向变量a的指针变量。既然pa是 变量,加么指针变量pa不但前以存放变量a的地址,也可以存放变量b的地址。有些书上把指针变量简称为指针。为了避免混 淆,我们约定:“指针”是指地址,是常量,“指 针变量”是指取值为地址的变量。定义指针的目的 是为了通过指针去访问内存单元。C语言程序设计与数据结构定义指针变量对指针变量的类型说明包括三个内容:指针类型说明,即定义变量为一个指针变量;A指针变量名;A变量值(指针)所指向的变量的数据类型。其一般定义形式为:类型说明符*变量名;其中,*表示其后面的变量是一个指针变量,变 量名即为定义的指针变量名,类型说明符表示指针 变量所指向的变量的数据类型。C语言程序设计与数据结构定义指针变量 制例如:int*pl;其中的*表示pl是一个指针变量,变量名为pL 它的值只能是某个整型变量的地址,或者说pl指向 一个整型变量。至于pl究竟指向哪一个整型变量,应由向pl赋予的地址来决定。但一个指针变量只能 指向同一种类型的变量,如下面的p3只能指向浮 点型变量(如前面的x或y)而不能指向字符型变量。再如:double*p2;/*p2是指向double型变量的指针变量*/float*p3;/*p3是指向浮点型变量的指针变量*/char*p4;/*p4是指向字符型变量的指针变量*/C语言程序设计与数据结构指针变量的赋值与引用指针变量同普通变量一样,使用之前不仅要定义说明,而 且必须赋予具体的值。未经赋值的指针变量不能使用,否 则将造成系统混乱,甚至死机。指针变量的赋值只能赋予 地址,决不能赋予任何其它数据,否则将引起错误。设已定义下面两个变量:int a;int*p,*q;/*定义指向整型变量的指针变量p和q*/如果想把整型变量a的地址赋予指针变量p,首先要知道a的 地址。C语言提供了一些运算符来实现此功能。1、取地址运算符&2、指针运算符*(或称“间接访问”运算符、取内容运算符)c语言程序设计与数据结构取地址运算符&在C语言中,变量的地址是由编译系统分配的,用户无需知道变量的具体地址。C语言中提供了专 门的取地址运算符&来表示变量的地址。取地址运算符&是单目运算符,其结合性为自右 至左,其功能是取变量的地址。其一般形式为:&变量名;1如&a表示变量a的地址。在前面章节的scanf函数 中,我们已经使用了&运算符。取地址运算符&只 能作用于对变量和数组元素,而不能作用于表达式、常量。C语言程序设计与数据结构要把整型变量a的地址赋予指针变量p,嬲可以有以下两种方式:指针变量初始化的方法int a;int*p=&a;俨定义指针变量p的同时对其进行初始化*/注意:是赋给了指针变量p,p前面的*表示p是一个指针变 量,变量名为p。赋值语句的方法int a=3;int*p;p=&a;这是一个赋值语句,被赋值的指针变量p前不能再 加“*”说明符,如写为*p=&a是错误的。也不允许把一个 数赋予指针变量,如p=2000;c语言程序设计与数据结构ap图8.2指针与它指向的变量&a-3C语言程序设计与数据结构【例8.1】取地址运算符&和指针变量的赋值。main()(int a,*p;p=&a;/*不能写成*p=&a;*/printf(p,%p,%x,%x,%dn”,&a9 p,&a9 p,&a);I运行结果如下:I0012FF7C,0012FF7C,12ff7c,12ff7c,1245052注意:具体的地址值可能与上述结果12FF7c不同,这跟机 器结构、操作系统、变量的类型及存储器的用法等有关。c语言程序设计与数据结构我们知道,一般情况下整型变量在内存中占2个字节;微 实型变量占4个字节;双精度实型变量占8个字节;字符型 变量占8个字节。既然指针变量是一种专门用来存放他人地 址的变量,那指针变量这种特殊的变量又在内存中占几个 字节呢?【例8.2】各种变量占用内存数比较。main()(int a,*p;float x=3.0;double m=3.124;char chl=!a ch2=b;printf(t!%d,%d,%d,%d,%d,%dn M,sizeof(int),sizeof(int*)5sizeof(float)5sizeof(double)5sizeof(char)5sizeof(p);c语言程序设计与数据结构运行结果如下:4,4,4,8,1,40012FF7C,0012FF78,0012FF74,0012FF6C,0012FF68,0012FF64 再看看图8.L是不是 这样呢?200020022004200820122020202123.44.53.124变量a 变量b变量式变量y变量m变量chl 变量ch2图8.1不同数据类型变量在内存中占用的空间C语言程序设计与数据结构指针运算符*指针运算符*(或称“间接访问”运算符、取内 容运算符),是单目运算符,其结合性为自右至左,用来表示指针变量所指向的变量。在*运算符之后 跟的变量必须是指针变量。C语言程序设计与数据结构【例8.3】取地址运算符&和指针运算符*的用法。main()int a=200,x=100/p;/*定义了两个可存放整数的整型变量a和x;还定义了一个只能存 放整型变量地址的指针变量P,但P还未指向任何整型变量*/p=&a;/*把a的地址赋给p,此时指针变量p指向整型变量a*/printf(p,%pn”,&a,p);/*指针变量p中存放的是a的地址*/printf(“a=%d,x=%d,*p=%d,*&a=%d n,a,x,*p,*&a);x=*p;/*把指针变量p指向的整型变量(即a)的值赋给x*/printf(ua=%d,x=%d,*p=%d,*&a=%d n”,a,x,*p,*&a);*p=300;/*把300赋给指针变量p指向的整型变量(即a)刃printf(ua=%d,x=%d,*p=%d,*&a=%d n”,a,x,*p,*&a);)运行结果如下:0012FF7c,0012FF7ca=200,x=100,*p=200,*&a=200a=200,x=200,*p=200,*&a=200a=300,x=200,*p=300,*&a=300c语言程序设计与数据结构需要注意的是指针运算符*和指针变量说明中的指针说 明符*不是一回事。在代码第3行中,int*p中的*是类型说 明符,表示其后的变量p是指针类型。而代码第7行x=*p中 的则是一个运算符用以表示指针变量p所指的变量a。假设变量a的地址为FF7c,例8.3代码第7行的赋值可形象 理解为图8.3所示的联系。X200图8.3关于x=*p的执行图示c语言程序设计与数据结构【例8.4从键盘输入两个整数,按由大到小的顺序输出。main()int*pl,*p2,aht;/*定义指针变量与整型变量*/printf(nInput ab:);scanf(n%d,%df&a,&b);pl=&a;p2=&b;/*使指针变量指向整型变量*/if(*plv*p2)/*交换指针变量指向的整型变量*/t=*pl;*pl=*p2;*p2=t;printf(nna=%d5b=%dnfa5b);printf(max=%d,min=%dn,*pl,*p2);在程序中,当执行赋值操作pl=&a和p2=&b后,指针分别指向了变量a与b,这时引用指针*pl写*p2,就代表了变量a与b。运行情况如下:Input a,b:3,4/a=4,b=3max=4,min=3C语言程序设计与数据结构在程序运行过程中,指针与所指的变量之间的关系 如图8.4所示,当指针被赋值后,其在内存的存放如(a),当数据比较后进行交换,这时,指针变量与所 指向的变量的关系如(b)所示,在程序的运行过程 中,指针变量与所指向的变量其指向始终没有变化。(a)(b)图8.4程序运行中指针与变量之间的关系c语言程序设计与数据结构El另外,既然指针变量和一般变量一样都是变量,就具有变 的性质一一存放在它们之中的值是可以改变的,也就是说 可以改变它们的指向。下面对【例8.4做一下修改。【例8.5】从键盘输入两个整数,按由大到小的顺序输出a和b。main()int*pl/p25a5b/t;printf(nInput a9b:scanf(%d,%d,&a,&b);pl=&a;p2=&b;if(*pl v*p2)t=pl;pl=p2;p2=t;/*指针交换,由于pl,p2均为指向整型变量的指针变量,因此可以相互赋值。刃printf(Mna=%d5b=%dn!a5b);printf(max=%d,min=%dn*pl,*p2);)c语言程序设计与数据结构运行情况如下:Input a,b:3,4/a=3,b=4max=4,min=3程序在运行过程中,实际存放在内在中的数据没有移动,而是将指向 该变量的指针交换了指向,如图85所示。当指针交换指向后,pl和p2 由原来指向的变凄a和b改变为指向变量b和a,这样一来,*pl就表示变 量b,而*p2就表小变量a。(a)(b)图8.5运行中指针与变量之间的关系C语言程序设计与数据结构指针变量可出现在表达式中设有:int x=3,y,*p=&x;即指针变量p指向整数x,则*p可出现在x能出 现的任何地方。则:y=*p+5;表示把*p即变量x的内容加5后赋给y,赋值后y值为8。变量x不变.*p=*p+3;表示把*p即变量x的内容加3后赋给x自己,赋值后x值为6C语言程序设计与数据结构【例8.6】通过变量的指针实现求两个数的和与积。#include main()int a=10,b=20,s,t,*pa,*pb;/*说明pa,pb为整型指针变量*/pa=&a;/*给指针变量pa赋值,pa指向变量a*/pb=&b;/*给指针变量pb赋值,pb指向变量b*/s=*pa+*pb;/*求 a+b 之和,(*pa 就是 a,*pb 就是 b)*/t=*pa*pb;/*本行是求a*b之积*/printf(Ha=%d,b=%d,a+b=%d,a*b=%dn”,a,b,a+b,a*b);printf(Hs=%d,t=%dnn,s5t);运行情况如下:a=10,b=20,a+b=30,a*b=200s=30,t=200c语言程序设计与数据结构8.2.3 指针变量作为函数参数函数的参数不仅可以是整型、实型、字符型等数 据,还可以是指针类型。它的作用是将一个变量的 地址传送到另一个函数中。c语言程序设计与数据结构【例8.7】将输入的两个整数按大 小顺序输出。要求用函数处理,而且用指针类型的数据作函数参 数。#include swap(int*pl,int*p2)(int temp;temp=*pl;*pl=*p2;*p2=temp;)main()int a,b;int pointer l/pointer!;printf(uInput a,b:0);scanf(n%d,%dn,&a,&b);pointer_l=&a;pointer_2=&b;if(ab)swap(pointer_l 5pointer!);printf(un%d5%dnfa5b);运行情况如下:Input a,b:3,4/a=4,b=3程序分析swap是用户定义的函数,它的作用是交换两个变量(a 和b)的值。swap函数的形参pl、p2是指针变量。程序运行 时,先执行main函数,输入a和b的值。然后将a和b的地址 分别赋给指针变量?ointer_l和pointer_2,使pointer_l指向a,pointer_2指向b。接着执行if语句,由了ab,因此我行 swap函数。注意实参pointer和poiuter_2是指针变量,在 函数调用时,将实参变量的宿传递给形头变量。采取的依 然是“值传递”方式。因此虚实结合后形参pl的值为&a,p2的值为&b。这时pl和pointer指向变量a,p2和 pointer_2指向变量b。接着执行swap函数的函数体使*pl和*p2的值互换,也就是使a和b的值互换。函数调用结束后,pl和p2不复存在(已释放)。最后在main函数中输出的a和 b的值是已经过交换的值。c语言程序设计与数据结构I 思考 t为什么不能用下面的函数swap2呢?swap!(int*p2)(int*temp;*temp=*pl;/*此语句有问题,temp指针没有指向明确的地址*/*pl=*p2;*p2=*temp;C语言程序设计与数据结构8.3 一维数组与指针 W每个数组包含一组具有同一类型的变量(即数 组元素),这些变量在内存中占有连续的存储单元,它们都有自己相应的地址。数组元素的指针就是数组元素的地址。而所谓数 组的指针则是指数组的起始地址。指针变量用来存放变量的地址,它可以指向变 量,当然也可存放数组的首地址或数组元素的地址。也就是说,指针变量可以指向数组或数组元素。C语言程序设计与数据结构指向数组元素的指针 粉一个数组是由连续的一块内存单元组成的。C语言规定,数组名就是 这块连续内存单元的首地址。而每个数组元素按其类型不同占有几个连 续的内存单元,每个数组元素的首地址就是该元素所占有的几个内存单 兀而起始施皿。设有:inta10;/*定义a为包含10个整型数据的数组*/int*p;/*定义p为指向整型变量的指针*/既然a的每个元素为int型,指针变量p又为指向int型的指针变量。则可 以对指针变量p做如下赋值:p=&a0;是把a0元素的地址赋给指针变量p,也就是说p指向a数组 的第0号元素。p=&a3;是把a3元素的地址赋给指针变量p。p=a;是什么意思呢?数组名代表数组的首地址,也就是第0号元素的 地址。上例中,a是数组名,它代表数组a的首地址,&a0是数组元素 a0的地址,由于a0的地址就是数组a的首地址,因此,p=&a0与p=a 而个赋值语句效臬完全相同。经过上述赋值p=&a而;或p=a;后,p,a,&a0均指向同一单元,它们都是数组a的首地址,也就是0号元素 a0的首地址。c语言程序设计与数据结构c语言规定:如果指针变量p已指向数组中的一个元素;则P+1指向同一数组中的下一个元素。这是因为P是指针变 量,它可以指向a0,也可以改变指向,比如再指向a3,如果有p=&a3,贝Up=p+1操作后,p改为指向a4。要注意 的是数组a的地址和&a0都是常量,不能改变。对于指向数组的指针变量,可以加上或减去一个整数n。设pa是指向数组a的指针变量,贝ljpa+n,pa-n,pa+,+pa,pa-,-pa运算都是合法的。指针变量加或减一个整 数n的意义是把指针指向的当前位置(指向某数组元素)向前 或向后移动口个位置。应该注意,数组指针变量向前或向后 移动一个位置和地址加 1或减 1在概念上是不同的。因为数 组可以有不同的类型,各种类型的数组元素所占的字节长 度是不同的。如指针变量加L即向后移动1个位置表示指 针变量指向下一个数据元素的首地址。而不是在原地址基 础上加1。c语言程序设计与数据结构例如:int a5,*pa;pa=a;/*pa指向数组a,也是指向a0*/pa=pa+2;/*pa指向a2,即pa的值为&pa2*/c语言程序设计与数据结构832 通过指针引用数组元素引用单个数组元素A引用连续的数组元素C语言程序设计与数据结构引用单个数组元素数组的每个元素与单个同类型变量的用法一样。【例8.8从键盘输入两个整数,按由大到小的顺序输出。#include main()int a2,t;/*定义指针变量与整型数组*/printf(uInput a0,al:J,);scanf(n%d,%dn,&aO,&al);pl=&a0;/*使指针变量指向整型数组元素*/p2=&al;if(*plv*p2)/*交换指针变量指向的整型数组元素*/t=*pl;*pl=*p2;*p2=t;printf(MaO=%d,al=%dnfaO,al);printf(nmax=%d5min=%diin/pl5*p2);运行情况如下:Input a09al:3,4/a0=4,al=3max=4,min=3C语言程序设计与数据结构【例8.9】将输入的两个整数按大小顺序输出。要求用函 数处理,而且用指针类型的数据作函数参数。#include swap(int*pl,int*p2)int temp;temp=pl;pl=*p2;p2=temp;main()运行情况如下:InputaO,al:3,4/a0=4,al=3 inta2pl/p2;printf(uInput a0闺 1:);scanf(n%d,%dn,&aO,&al);pl=&aO;p2=&al;if(aOal)swap(pl,p2);printf(nnaO=%d,al=%dnfaOLal);)c语言程序设计与数据结构引用连续的数组元素 赢假设a数组有10个元素,如果已做赋值操作p=&a0;则有:p+n与a+n都表示数组元素an的地址,即对整个a 数组来说,共亘10个元素,n的取值为09,则各数组元素 的地址可以表示为ptr+0ptr+9或a+0a+9,与&a0&a 9完全一致。(2)*(p+n)或*(a+n)就是p+n或a+n所指向的数组元素,即an。例如*(p+5)或*(a+5)就是 a5。(3)指向数组的指针变量也可以带下标,如pi与*(p+n)或an 等价。但要注意,如果做赋值操作p=&a2,则有:H(1)p+n与a+n+2都表示数组元素an+2的地址,即&an+2。n的取值为07,则数组元素a2a9的坡址可以表示为 ptr+0ptr+7或a+2a+9,与&a3莅a9完全一致。(2)*(p+n)或*(a+n+2)就是p+n或a+n+2所指向的数组元素,即 an+2 o 例如*(p+5)或*(a+5+2)就是a7。c语言程序设计与数据结构【例8.10】输出数组中的全部元素。(利用数组下标)main()int a5J;for(i=0;i5;i+)ai=i;/*此处可以改为 scanf(“d”,&ai);I 来随机输入5个整数*/for(i=0;i5;i+)printf(na%d=%d,闺i);c语言程序设计与数据结构【例8.11】输出数组中的全部元素。(通过数组名计 算各元素的地址,找出元素的值)main()(int a5J;for(i=0;i5;i+)*(a+i)=i;/*此处可以改为scanf(d”,a+i);来随机输入5个整数*/for(i=0;i5;i+)printf(na%d=%dnnJ/(a+i);C语言程序设计与数据结构【例8.12】输出数组中的全部元素。(用指针变量,注意p一 直指向a的首地址)main()(int a5J/p;P=a;for(i=0;i5;i+)*(p+i)=i;/*此处可以改为scanf(d”,p+i);来随机输入5个整数*/for(i=0;i5;i+)printf(na%d=%dnfi/(p+i);c语言程序设计与数据结构【例8.13】输出数组中的全部元素。(用指针变量,的数组元素)main()注意p不断改变指图4int a5,i,*p=a;for(i=0;i5;i+)*p=i;/*此处可以改为scanf(d”,p);来随机输入5个整数*/P+;/*指针变量p可以实现本身的值的改变,此处p+是合法的;而a+是错误的,因为a是数组名,它是数组的首地址,是常量*/)p=a;/*指针变量p通过上面的多次p+,已不再指向a数组的首地址。需要重新赋值,使p再次指向a数组的首地址*/for(i=0;i5;i+)f iprintf(na%d=%dnn,i,*p);P+;c语言程序设计与数据结构【例8.14本例能否达到和例811同样的效果?为什么?如不能则找出其中的错误。main()(int*p,i,a5;P=a;P=a;for(i=0;i5;i+)*p+=i;for(i=0;ipf2表示pfl处于高地址位置pflvpf2表示pf2处于低地址位置(6)指针变量还可以与0比较。设p为指针变量,则p=0表明p是空指针,它不指向任何变 量;p!=0表示p不是空指针。空指针是由对指针变量赋予0 值而得到的。如:int*p=NULL;NULL是个宏,一般在系统头文件stdio.h中会有这样的宏定义如下:#define NULL 0对指针变量赋0值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的。否则将造成意外错误。而 指针变量赋0值后,则可以使用,只是它不指向具体的变量 而已。c语言程序设计与数据结构8.3.3 一维数组名作函数参数数组名可以作函数的实参和形参。数组名就是数组的首地 址,实参向形参传送数组名实际上就是传送数组的地址,形参得到该地址后也指向同一数组。同样,指针变量的值 也是地址,数组指针变量的值即为数组的首地址,当然也 可作为函数的参数使用。如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参的对应关系有以下4种,例题8.15例题8.18分 别对其进行了演示:(1)形参和实参都是数组名.(2)实参用数组,形参用指针变量。(3)实参、型参都用指针变量。(4)实参为指针变量,型参为数组名。C语言程序设计与数据结构【例8.15】将数组a中的n个整数按相反顺序存放。(形参和实参都是耦 组名)算法为:将a0与对换,再al与an-2对换,直到将a(n.l/2)与an-int(n-l)/2)对换。今用循环处理此问题,设两个“位置指示变 量”i和j,i的初值为0,j的初值为n-L将ai与aj交换,然后使i的 值加1,j的值减1,再将ai与aj交换,直到i=(n-l)/2为止。程序如下:main()void inv(int x,int n)/*形参x是数组名*/int i,a10=3,7,9,ll,0,6,7,5,4,2;int temp,i,j,m=(n-1)/2;for(i=0;i=m;i+)j=n-l-i;temp=xi;xi=xj;xj=temp;printf(nThe original array:nn);for(i=0;il 0;i+)printf(n%d;ai);printf(nnH);inv(a,10);/*实参a是数组名*/printf(nThe array has benn inverted:nn);for(i=0;i10;i+)printf(n%d;ai);printf(nnH);C语言程序设计与数据结构【例8.16】对例8.15作一些改动。实参用数组,形参用指针变量。程序如下:void inv(int*x,int n)/*形参x为指针变量*/int*p,tempi/j,m=(n-l)/2;i=x;j=x+n-l;p=x+m;for(;i=p;i+,j-)temp=*i;*i=*j;*j=temp;return;)main()int i,a10=3,7,9,H,0,6,7,5,4,2;printf(nThe original array:nn);for(i=0;i10;i+)printf(n%d;ai);printf(nnn);inv(a10);/*实参a是数组名刃printf(nThe array has benn inverted:nn);for(i=0;i10;i+)printf(n%d;ai);printf(nnn);)C语言程序设计与数据结构【例8.17】输入5个分数,求出平均分。要求实参、型参都用指针变量。float aver(float*pa);main()float sco5,av/sp;int i;sp=sco;printf(nninput 5 scores:nH);for(i=0;i5;i+)scanf(u%f&scoi);av=aver(sp);printf(naverage score is%5.2f n!av);)float aver(float*pa)(int i;float av,s=0;for(i=0;i5;i+)s=s+*pa+;av=s/5;return av;)C语言程序设计与数据结构【例88】输入5个分数,求出平均分。要求实参用指针变量,型参是数组。float aver(float pa);main()float sco55av/sp;int i;sp=sco;printf(nninput 5 scores:nn);for(i=0;i5;i+)scanf(n%fn,&scoi);av=aver(sp);printf(naverage score is%5.2f nfav);float aver(float pa)(int i;float av5s=0;for(i=0;i5;i+)s=s+*pa+;av=s/5;return av;c语言程序设计与数据结构.4二维数组与指针8.4.1 引用单个数组元素8.4.2 指向二维数组的指针变量8.4.3 指向多维数组的指针变量C语言程序设计与数据结构8.4.1引用单个数组元素int a23;/*定义a为两行三列的二维数组*/I int*p;/*定义p为指向整型变量的指针刃 BI既然a的每个元素为int型,指针变量p又为指向 int型的指针变量。则可以对指针变量p做赋值:p=&a02;就是把a0H2元素的地址赋给指针变量P。C语言程序设计与数据结构【例89】把二维数组叨23第一行和第二行的值互换。main()int*pl,*p2,t,j,k,a23=l,2,3,4,5,6;for(j=0;j3;j+)(pl=&aOj;/*使指针变量指向整型数组元素*/p2=&alD;t=*pl;/*交换指针变量指向的整型数组元素*/=*p2;*p2=t;for(j=0;j2;j+)|for(k=0;k3;k+)printf(n%d n,ajk);printf(ai2 组成的一维数组。ai+j 等价于&aij(0=j=3)*(ai+j)等价于 aij所以*(a+i)+j等价于&aij,*(*(a+i)+j)等价于c语言程序设计与数据结构atOZI at。:aDDll aClIIJC2J E2J E2 alZN:aEOaLlaL2a图8.6二维数组在内存中的存放形式C语言程序设计与数据结构分析我们定义的二维数组a其元素类型为整型,每个元素在内 存占两个字节,若假定二维数组a从1000单元开始存放,则 按照按行存放的原则,数组元素在内存的存放地址为 10001022。若用地址法来表示数组各元素的地址,对元 素来说,&叨12是其地址,漆1+2也是其地址。分 析al+l与al+2的地址关系,它们地址的差并非整数1,而是一个数组元素所占的字节数2,原因是每个数组元素占 两个字节。对0行首地址与1行首地址a与a+1来说,地址的 差同样也并非整数1,而是一行共四个元素所占的字节数8。前面介绍过,C语言允许把一个二维数组分解为多个一 维数组来处理。因此数组a3H4可分解为三个一维数组,即 a0,al,a2o每一个一维数组又含有四个元素。?如 a0数组,含有a0l,a02,a0H3四个元素。C语言程序设计与数据结构从二维数组的角度来看,a是二维数组名,a代表整个 二维数组的首地址,也是二维数组0行的首地址,等于 lOOOo a+1代表第一行的首地址,等于1008。a0是第一 个一维数组的数组名和首地址,因此也为1000。*(a+0)或*a是与a0等效的,它表示一维数组a00号元素的首 地址,也为1000。&a0H。是二维数组a的0行0列元素的 首地址,同样是1000。因此,a,a0,*(a+0),*a,&a0H0是相等的。同理,a+1是二维数组1行的首地址,等于1008。al是第二个一维数组的数组名和首地址,因此也为1008。是二维数组a的1行0列元素地址,也是1008。因此a+1闺是等同的。由此 可得出:a+i,ai,*(a+i),是等同的。此外,&ai和ai也是等同的。因为在二维数组中不 能把&ai理解为元素ai的地址,不存在元素ai。C语 言规定,它只是一种地址计算方法,表示数组a第i行首 地址。由此,我们得出:ai,&ai,*(a+i)和a+i也都是 等同的。C语言程序设计与数据结构另外,a0也可以看成是a0+0,是一维数组a0的0 号元素的首地址,而aO+l则是a0的1号元素首地址,由此可得出ai+j则是一维数组ai的j号元素首地址,它 等于由ai=*(a+i)得ai+j=*(a+i)+j。由于*(a+i)+j是二维数组a的i行j列元素的首地址,所以,该元 素的值等于*(*(a+i)+j)。由于数组元素在内存的连续存放,如给指向整型变量 的指针赋以数组的首地址,则该指针指向二维数组。如:int*ptr,a34;若赋值:ptr=aO;则用ptr+就能访问数组的各元素。C语言程序设计与数据结构【例8.20】用地址法输入输出二维数组各元素。#include main()(inta34,i,j;printf(nInput 12 ge integer:nH);for(i=0;i3;i+)for(j=0;j4;j+)scanf(n%dn,ai+j);/*地址法*/for(i=0;i3;i+)for(j=0;j4;j+)printf(”4d”,*(ai+j);/*此处*(ai+j)是地址法所表示的数组元素*/printf(nnn);运行结果为:1234567891011 12/12 3 45 6 7 89 1011 12C语言程序设计与数据结构【例8.21】用指针法输入输出二维数组各元素。#includemain()inta34,i,j/ptr;ptr=aO;printf(nInput 12 ge integer:nu);for(i=0;i3;i+)for(j=0;j4;j+)scanf(n%dn,ptr+);/*指针的表示方法刃ptr=aO;for(i=0:i3:i+)for(j=0;jv4:j+)printf(%4d1*ptr+);)C语言程序设计与数据结构运行结果为:旅j1234567891011 12/12 3 45 6 7 89 10 11 12对指针法而言,上述程序中的for循环阴影部分也可以把 二维数组看作是展开的一维数组:for(i=0;i12;i+)printf(n%4dn/ptr+);printf(HnH);运行结果为:1234567891011 12/123456789 10 11 12c语言程序设计与数据结构8.4.3指向多维数组的指针变量把二维数组a分解为一维数组a0,al,a2之后,设p为指 向二维数组的指针变量。可定义为:int(*p)4;它表示p是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组a0,其值等于a,a0,或&叨00 等。而p+i则指向一潍数组aji。从前面的分析可得出*(p+i)+j是二维数组i行j列的元素的地址,而*(*(p+i)+j)则 是i行j列元素的值。二维数组指针变量说明的一般形式为:类型说明符(*指针变量名)长度其中“类型说明符”为所指数组的数据类型。表示 其后的变量是指针类型。“长度”表示二维数组分解为多 个一维数组时,一维数组的长度,也就是二维数组的列数。应注意(*指针变量名)”两边的括号不可少,如缺少括号 则表示是指针数组(本章后面介绍),意义就完全不同了。C语言程序设计与数据结构【例8.22】main()int a34=0,1,2,3,4,5,6,7,8,9,10,11;int Cp)4;int ij;P=a;for(i=0;i3;i+)(for(j=0;jb)return a;else return b;)main()(int max(int a,int b);int(*pfun)();int x,y,z;pfun=max;printf(ninput two numbers:nn);scanf(n%d%dn,&x,&y);z=(*pfun)(x,y);printf(nmax num=%dz);c语言程序设计与数据结构从上述程序可以看出用,函数指针变量形式调用 函数的步骤如下:(1)先定义函数指针变量,如main函数第4行int(*pfun)();定义pfun为函数指针变量。(2)把被调函数的入口地址(勺数名)赋予该函数 指针变量,如main函数第6行pfun=max;(3)用函数指针变量形式调用函数,如main函 数第9行z=(*pfun)(x9y);调用时(*pfun)的两边 的括号不可少,其中的*不应该理解为求值运算,在此处它只是一种表示符号。(4)调用函数的一般形式为:(*指针变量名)(实参表),如z=(*pfun)(x,y);C语言程序设计与数据结构8.7 指针型函数前面我们介绍过,所谓函数的类型是指函数返回值的类型。在c语 言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函 数称为指针型函数。定义指针型函数的一般形式为:类型说明符*函数名(形参表)./*函数体*/其中函数名之前加了 号表明这是一个指针型函数,即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。如:int*ap(int x,int y)(/*函数体代码*/表示ap是一个返回指针值的指针型函数,它返回的指针指向一个整 型变量。c语言程序设计与数据结构【例8.29】#include int x=8,y=9;/*定义全局变量x和y*/int*funmax()/*该函数返回全局变量x和y中的大者的地址*/if(xy)return&x;else return&y;main()(int*pmax;pmax=funmax();printf(nMax num=%d nn/pmax);c语言程序设计与数据结构应该特别注意的是函数指针变量和指针型函费 这两者在写法和意义上的区
展开阅读全文