第6章 指针教学课件PPT.ppt

上传人:春哥&#****71; 文档编号:25342948 上传时间:2022-07-11 格式:PPT 页数:79 大小:3.20MB
返回 下载 相关 举报
第6章 指针教学课件PPT.ppt_第1页
第1页 / 共79页
第6章 指针教学课件PPT.ppt_第2页
第2页 / 共79页
点击查看更多>>
资源描述

《第6章 指针教学课件PPT.ppt》由会员分享,可在线阅读,更多相关《第6章 指针教学课件PPT.ppt(79页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、完整版教学课件完整版教学课件第6章 指针 第6章 指针u6.1 寻觅芳踪:初识指针寻觅芳踪:初识指针u6.2 强强联合:指针和函数强强联合:指针和函数u6.3 灵活高效:指针和数组灵活高效:指针和数组u6.4 本章小结本章小结屠龙刀倚天剑强转与指针,并称C语言的两大神器用好了可呼风唤雨,威力无比用不好也会伤及自身 6.1 初识指针初识指针n理解指针要从变量的地址谈起n是谁惹的祸?n几乎全是由指针和数组引起的非法内存访问导致的n黑客攻击服务器利用的bug绝大部分都是指针和数组造成的 6.1 初识指针初识指针6.1变量的内存地址变量的内存地址变量的地址变量的地址(Address)变量名变量名内存:

2、计算机内的存储部件内存:计算机内的存储部件所有指令和数据都保存在内存里所有指令和数据都保存在内存里速度快,可随机访问,但掉电即失速度快,可随机访问,但掉电即失编译或函数调用时为变量分配内存单元编译或函数调用时为变量分配内存单元0000ContentsContentsContentsContentsContentsContentsContents变量的值变量的值某存储区域6.1.1 内存地址和指针内存地址和指针 6.1 初识指针初识指针 6.1变量的内存地址变量的内存地址变量的地址变量的地址内存中的每个字节都有唯一的编号(地址)内存中的每个字节都有唯一的编号(地址)地址是一个十六进制无符号整数地

3、址是一个十六进制无符号整数其字长一般与主机相同其字长一般与主机相同地址按字节编号,按类型分配空间地址按字节编号,按类型分配空间0000ContentsContentsContentsContentsContentsContentsContents某存储区域Address Operator 6.1 初识指针初识指针如何读写内存中的数据?如何读写内存中的数据?0000ContentsContentsContentsContentsContentsContentsContents某存储区域只要指明要访问的变量的内存单元地址只要指明要访问的变量的内存单元地址就可以立即访问到变量所在的存储单元就可以立即

4、访问到变量所在的存储单元scanf(%d, &a); 6.1 初识指针初识指针【例例】使用取地址运算符使用取地址运算符& &取出变量的地取出变量的地址,然后将其显示在屏幕上。址,然后将其显示在屏幕上。表示输出变量表示输出变量a的地址值的地址值 6.1 初识指针初识指针如何读写内存中的数据?如何读写内存中的数据?0000ContentsContentsContentsContentsContentsContentsContents某存储区域直接寻址:直接寻址:按变量地址存取变量值按变量地址存取变量值scanf(%d, &a); 6.1 初识指针初识指针 6.1 初识指针初识指针如何读写内存中的数

5、据?如何读写内存中的数据?0000ContentsContentsContentsContentsContentsContents0 x0037b000某存储区域间接寻址:间接寻址:通过存放变量地址的变量去访问变量通过存放变量地址的变量去访问变量n 存放变量的地址需要一种特殊类型的变量存放变量的地址需要一种特殊类型的变量指针指针(Pointer)类型类型n 指针变量指针变量具有指针类型的变量具有指针类型的变量 指针变量指针变量 指向指向 变量变量变量的地址(指针)变量的地址(指针)变量值变量值变量地址存入变量地址存入指针变量指针变量 6.1 初识指针初识指针指针变量的定义v一般形式: 存储类型

6、 基类型类型 * 指针变量名;合法标识符指针变量本身的存储类型指针所指变量(目标变量)的数据类型例 int * p1,* p2; float * q ; static char * name;注意:注意: 区别区别int int * * p1, p1,* * p2; p2;与与int int * * p1,p2; p1,p2; 指针变量名是指针变量名是p1,p2 ,p1,p2 ,不是不是* *p1,p1,* *p2p2 指针变量只能指向定义时所规定类型的变量指针变量只能指向定义时所规定类型的变量 指针变量定义后,变量值不确定,应用前必须先赋值指针变量定义后,变量值不确定,应用前必须先赋值。 6

7、.1 初识指针初识指针6.1.2 指针变量的定义、初始化与引用指针变量的定义、初始化与引用 指针变量的初始化指针变量的初始化一般形式:一般形式:存储类型存储类型 数据类型数据类型 * 指针名指针名=初始地址值初始地址值;例 int i; int * p=&i;类型的一致性例 int i; int * p=&i; int * q=p;用已初始化指针变量作初值 6.1 初识指针初识指针int i,* p;p=&i; int * p;float * q;p=q;int i;float * p;p=&i;int * p;p=100;指针变量只指针变量只存放地址存放地址!一个指针变量不能指向与一个指针变

8、量不能指向与其类型不同的变量其类型不同的变量!我是真的,我是真的,你猜对了吗?你猜对了吗?应在类型相同的应在类型相同的指针变量之间赋指针变量之间赋值!值! 【案例【案例6.1】 打印普通变量的地址值和指针变量的值。打印普通变量的地址值和指针变量的值。#include int main()()int x, y;int * pX=&x; /*定义指针变量定义指针变量pX,并将其赋值为变量,并将其赋值为变量x的地址的地址*/int * pY, * pZ; /*定义指针变量定义指针变量pY与与pZ*/pY=&y; /*将变量将变量y的地址赋给指针变量的地址赋给指针变量pY*/ pZ=pY; /*将指针

9、将指针pY的值赋给指针的值赋给指针pZ,让,让pZ指向指向pY,二者指向同一内存空间,二者指向同一内存空间*/printf( (&x=%p, pX=%pn,&x,pX) );/*打印变量打印变量x的地址和指针变量的地址和指针变量pX的值的值*/printf( (&y=%p, pY=%pn,&y,pY) );/*打印变量打印变量y的地址和指针变量的地址和指针变量pY的值的值*/ printf( (pZ=%pn,pZ) ); /*打印指针变量打印指针变量pZ的值的值*/ return 0;变量变量x的地址和指针变量的地址和指针变量pX的值相等,变的值相等,变量量y的地址和指针变量的地址和指针变量p

10、Y的值也相等,这的值也相等,这也证明了指针变量也证明了指针变量pX指向变量指向变量x,指针变,指针变量量pY指向变量指向变量y。而指针变量。而指针变量pZ与与pY指指向同一个内存空间,由此二者的地址值向同一个内存空间,由此二者的地址值相等。相等。 6.1 初识指针初识指针零指针与空类型指针v零指针(空指针): 指针变量值为零l表示: int * p=0; p指向地址为0的单元,系统保证该单元不作它用表示指针变量值没有意义#define NULL 0int * p=NULL;lp=NULL与未对p赋值不同l用途: u避免指针变量的非法引用u在程序中常作为状态比较 例 int * p; . whi

11、le(p!=NULL) . vvoid *类型指针l表示: void * p; l使用时要进行强制类型转换例 char * p1; void * p2; p1=(char *)p2; p2=(void *)p1;表示不指定p是指向哪一种类型数据的指针变量 6.1 初识指针初识指针访问指针变量访问指针变量所所指向指向的的变量,只需在指针变量前加一个变量,只需在指针变量前加一个“*”(取值运算符取值运算符) ),其,其格式格式为为:*指针表达式指针表达式;例如:例如:int i=0;/*定义并初始化变量定义并初始化变量i*/int * p=&i; /*定义指针变量定义指针变量p,将变量,将变量i的

12、地址赋给变量的地址赋给变量p,此时,此时p指向变量指向变量i*/printf( (%d, *p) ); /*打印指针变量打印指针变量p所指向内存空间中存放的值,也就是打印所指向内存空间中存放的值,也就是打印i的值的值*/此时的此时的*p就相当于就相当于i,如果再有一句赋值,如果再有一句赋值“*p=1;”就相当于完成赋值就相当于完成赋值“i=1; ”。运算符运算符&( (取地址运算符取地址运算符) )和和“*”互为逆运算,很显然,若定义指针变量互为逆运算,很显然,若定义指针变量p,则,则&( (*p) )与与p是等价的。是等价的。 6.1 初识指针初识指针例 int main( ) int i=

13、10; int * p; *p=i; printf(“%d”,*p); return 0; 危险!危险!例 int main( ) int i=10,k; int * p; p=&k; *p=i; printf(“%d”,*p); return 0; 指针变量必须先赋值,再使用.2000200420062005整型变量整型变量i10指针变量指针变量p200120022003随机随机 6.1 初识指针初识指针6.1.3 6.1.3 指针变量的移动和比较指针变量的移动和比较指针变量可以加上或减去一个整数指针变量可以加上或减去一个整数( (常量或变量常量或变量) ),达到改变地址值的目的。,达到改变

14、地址值的目的。在这一过程中,可以认为是指针变量发生了移动。在这一过程中,可以认为是指针变量发生了移动。若若p是一个指针变量,则是一个指针变量,则p+1表示指向下一个内存空间。需要注意的是,执行表示指向下一个内存空间。需要注意的是,执行p+1时,并不是将时,并不是将p的值的值( (地址地址) )进行简单的加进行简单的加1,而是加上,而是加上p的基类型占用的字节的基类型占用的字节数。数。例如,如果例如,如果p的基类型是的基类型是int类型,占类型,占4字节,则字节,则p+1意味着意味着p的值加的值加4字节,字节,p-1意味着意味着p的值减的值减4字节。变量字节。变量a的地址是的地址是0001,p的

15、值也是的值也是0001,当执行,当执行“p = p+1”时,由于时,由于p的基类型是的基类型是int型,在内存中占型,在内存中占4字节,因此执行后,字节,因此执行后,p就指向了就指向了“0001+4字节字节”后面的位置,即地址后面的位置,即地址0005的位置。的位置。若若y是整数,是整数,p是一个指针变量,如是一个指针变量,如果它所指向的数据类型在内存中占据果它所指向的数据类型在内存中占据x字节的存储空间,则字节的存储空间,则p+y表示在表示在p的地的地址值基础上址值基础上向后移动向后移动 x*y字节。反之,字节。反之,p-y表示在表示在p的地址值基础上的地址值基础上向前移动向前移动 x*y字

16、节。因此,不同基类型的指针变字节。因此,不同基类型的指针变量量p,在同样执行,在同样执行p+1后,其结果也是后,其结果也是不同的。不同的。 6.1 初识指针初识指针指针的自增指针的自增/自减运算类似于普通变量的自增自减运算。例如自减运算类似于普通变量的自增自减运算。例如“p+”与与“+p”就相当于就相当于“p=p+1”,“-p”与与“p-”相当于相当于“p=p-1”。此外,结合运算优先级,。此外,结合运算优先级,*p+等价于等价于*( (p+) ),作用是先得到,作用是先得到p所指向的变量的值所指向的变量的值( (即即*p) ),然后使,然后使p加加1。要注。要注意的是,它与意的是,它与*+p

17、不同,不同,*+p等价于等价于*( (+p) ),它是先使,它是先使p加加1,再取得,再取得*p的值。类似的值。类似地,地,*( (p-) )与与*( (-p) )的含义也是不同的。的含义也是不同的。【案例【案例6.2】 利用指针实现变量值的变化。利用指针实现变量值的变化。#include int main()() int a=5,* p; /*定义指针变量定义指针变量p*/ p=&a; /*让指针让指针p指向指向a*/ printf( (a=%d,*p=%dn,a,*p) ); /*输出输出a的值和的值和p所指向变量的值所指向变量的值*/ *p=8; /*对对p所指向的变量赋值,就相当于对所

18、指向的变量赋值,就相当于对a赋值赋值*/ printf( (a=%d,*p=%dn,a,*p) ); printf( (Enter a:) ); scanf( (%d,&a) ); printf( (a=%d,*p=%dn,a,*p) ); ( (*p) )+; /*将将p所指向的变量值加所指向的变量值加1,也就是将,也就是将a的值加的值加1*/ printf( (a=%d,*p=%dn,a,*p) ); return 0; 6.1 初识指针初识指针 如果是同类的指针进行相减运算,其结果是两个指针的地址之差除以指针基如果是同类的指针进行相减运算,其结果是两个指针的地址之差除以指针基类型所占字节

19、数。例如,整型指针变量类型所占字节数。例如,整型指针变量p1指向的变量的地址为指向的变量的地址为20000,整型指针,整型指针变量变量p2 指向的变量的地址为指向的变量的地址为20016,那么,那么,p2-p1的结果是的结果是“(20016-20000) )/4=4”,表示指针变量,表示指针变量p2所指的元素与所指的元素与p1所指的元素之间相隔所指的元素之间相隔4个同类型个同类型元素。元素。 既然指针可以移动,也就相应地存在大小比较,存在着关系运算。常用的关既然指针可以移动,也就相应地存在大小比较,存在着关系运算。常用的关系运算符系运算符,如如“=”、“!=”、“”、“=”等都适用于指等都适用

20、于指针。针。 在实际编程时,对单独零散的变量的指针进行加减运算和比较的意义不是太在实际编程时,对单独零散的变量的指针进行加减运算和比较的意义不是太大,但与批量数据的处理大,但与批量数据的处理(如数组等如数组等)结合使用时,指针这些的运算就有很大的结合使用时,指针这些的运算就有很大的作用了作用了。 注意:注意:只有同类指针之间才可以进行关系运算,对两个毫无关联的指针比较只有同类指针之间才可以进行关系运算,对两个毫无关联的指针比较大小是没有意义的,如指向两个毫不相干内存空间大小是没有意义的,如指向两个毫不相干内存空间(如两个不同的数组如两个不同的数组)的指针的指针之间无法比较大小。此外,不能对同类

21、指针进行相加运算,若存在指针变量之间无法比较大小。此外,不能对同类指针进行相加运算,若存在指针变量p1与与p2,则,则p1+p2没有任何意义。没有任何意义。 6.1 初识指针初识指针 6.2 指针与函数指针与函数6.2.1 6.2.1 指针变量作为函数参数指针变量作为函数参数【案例【案例6.3】 有张三和李四两位小朋友,张三手上拿着有张三和李四两位小朋友,张三手上拿着“书书”,李四手上,李四手上拿着拿着“画画”,由于张三非常喜欢李四的画,而李四也十分喜欢张三的书,因,由于张三非常喜欢李四的画,而李四也十分喜欢张三的书,因此他们决定此他们决定 “换一换换一换”,试利用程序实现这一交换的场景。,试

22、利用程序实现这一交换的场景。#define BOOK 0 /*定义书为定义书为0*/#define PICTURE 1 /*定义画为定义画为1*/#include swap( (int x, int y) ) int temp; temp=x; x=y; y=temp;int main()() int a=BOOK, b=PICTURE; swap( (a,b) ); if(a=PICTURE) )&( (b=BOOK) printf( (SUCCESSn) ); else printf( (FAILUREn) ); return 0;普通变量作为函数参数普通变量作为函数参数【案例【案例6.3

23、 改造改造1】 利用利用指针变量做指针变量做函数函数参数参数实现交换。实现交换。#include #define BOOK 0 /*定义书为定义书为0*/#define PICTURE 1 /*定义画为定义画为1*/swap( (int * x, int * y) ) int temp; temp=*x; *x=*y; *y=temp;int main()() int a=BOOK, b=PICTURE; swap( (&a,&b) ); if(a=PICTURE) )&( (b=BOOK) printf( (SUCCESSn) ); else printf( (FAILUREn) ); re

24、turn 0; 6.2 指针与函数指针与函数【案例【案例6.3 改造改造2】有风险的交换代码。有风险的交换代码。swap( (int *x, int *y) ) int * temp; *temp=*x; *x=*y; *y=*temp; 语句语句“int * temp;”后没有立即对指针变量后没有立即对指针变量temp初始化,它可能指向系统初始化,它可能指向系统重要数据的区域,而后马上执行一句赋值重要数据的区域,而后马上执行一句赋值“*temp=*x;”,就会将系统重要,就会将系统重要区域的值覆盖,从而导致系统崩溃。因此,这种对未知单元写操作是很危险区域的值覆盖,从而导致系统崩溃。因此,这种

25、对未知单元写操作是很危险的。可将的。可将 “int * temp;”修改为修改为“int * temp, t; temp=&t;”,其中新定义的,其中新定义的t一定被分配在空闲的内存空间中,这就比较安全了。一定被分配在空闲的内存空间中,这就比较安全了。 因此因此,在进行指针的使用时,要时刻注意以下三大原则在进行指针的使用时,要时刻注意以下三大原则: (1)永远要清楚每个指针指向了哪里。永远要清楚每个指针指向了哪里。 (2)永远要明确指针所指向单元的内容是什么。永远要明确指针所指向单元的内容是什么。 (3)永远不要使用未初始化的指针变量。永远不要使用未初始化的指针变量。 6.2 指针与函数指针与

26、函数swap( (int *x, int *y) ) int * temp,t; temp=&t; *temp=*x; *x=*y; *y=*temp;【案例【案例6.3 改造改造3】 错误的交换代码。错误的交换代码。swap( (int * x, int * y) ) int *temp; temp=x; x=y; y=temp; 此时在函数中只是通过指针此时在函数中只是通过指针temp交换了形参指针本身的值,不是交换了形参指针本身的值,不是指针所指向单元的内容,形参指针所指向单元的内容,形参x与与y的改变不会影响到实参的改变不会影响到实参a和和b,达,达不到交换的目的不到交换的目的。 可见

27、,要通过函数的调用改变主调函数中某个变量的值,正确的可见,要通过函数的调用改变主调函数中某个变量的值,正确的做法是做法是使用指针作为函数的参数使用指针作为函数的参数。在主调函数的一方,将该变量的。在主调函数的一方,将该变量的地址或指向该变量的指针作为实参;在被调函数的一方,使用指针地址或指向该变量的指针作为实参;在被调函数的一方,使用指针变量作为形参接收该变量的地址,并改变形参所指向变量的值。变量作为形参接收该变量的地址,并改变形参所指向变量的值。 6.2 指针与函数指针与函数6.2 指针与函数指针与函数6.2.2 6.2.2 返回值指针值的函数返回值指针值的函数 类型标识符类型标识符 * 函

28、数名函数名(参数列表参数列表) 函数体函数体; 例如:例如:int * func()() int * p; return p;说明说明func()()函数的返回值是一个指向整型变量的函数的返回值是一个指向整型变量的指针,是一个地址。指针,是一个地址。6.2 指针与函数指针与函数【案例【案例6.4】 输出变量输出变量a、b中较大值的地址。中较大值的地址。#include int * fp( (int x,int y) ) /*定义返回指针值的函数定义返回指针值的函数*/ if( (xy) ) return &x; /*返回变量返回变量x的地址值的地址值*/ else return &y; /*返

29、回变量返回变量y的地址值的地址值*/int main()() int a=2,b=3; int * p; p=fp( (a,b) ); /*接收变量接收变量a、b中较大值的地址,并赋值给指针变量中较大值的地址,并赋值给指针变量p*/ printf( (%pn,p) ); return 0;6.2 指针与函数指针与函数6.2.3 函数指针函数指针 如果有若干个整型数,要利用函数求出这些数的最大值、最小值如果有若干个整型数,要利用函数求出这些数的最大值、最小值及相加后的和,可定义函数及相加后的和,可定义函数max()()、min()()、add()()分别实现。在这分别实现。在这一过程中,对主调函

30、数也必须进行一过程中,对主调函数也必须进行3次调用,这种程序的通用性和简次调用,这种程序的通用性和简洁性都不好。如果我们能够提供一个类似统一的洁性都不好。如果我们能够提供一个类似统一的“模板模板”,在实际调,在实际调用时可以根据需要动态地指向某一个函数,实现在同一点调用不同的用时可以根据需要动态地指向某一个函数,实现在同一点调用不同的函数,就会减少重复的代码函数,就会减少重复的代码,这种想法利用这种想法利用“函数指针函数指针”就能实现。就能实现。 函数指针就是指向函数的指针,该指针中存储的是一个函数在内函数指针就是指向函数的指针,该指针中存储的是一个函数在内存中的存中的入口地址入口地址。由于程

31、序和数据共同存储在内存中,而函数作为子。由于程序和数据共同存储在内存中,而函数作为子程序也必然被存储在内存中,某一个函数第一条指令的地址就是该函程序也必然被存储在内存中,某一个函数第一条指令的地址就是该函数的入口地址。正如数组名代表了数组的首地址,函数名也可以代表数的入口地址。正如数组名代表了数组的首地址,函数名也可以代表函数源代码在内存中的起始地址,因此,编译器将不带函数源代码在内存中的起始地址,因此,编译器将不带“()”的函数的函数名名(如如max、min、add等等) )解释为函数的入口地址。解释为函数的入口地址。6.2 指针与函数指针与函数(1)定义一个指向函数的指针变量定义一个指向函

32、数的指针变量 格式:类型标识符格式:类型标识符 (* 指针变量名指针变量名) (参数列表参数列表); “类型标识符类型标识符”是指针变量所指向的函数的返回值类型,是指针变量所指向的函数的返回值类型,“参数列表参数列表”是所是所指向的函数的形式参数的列表。指向的函数的形式参数的列表。 例如:例如:int (*p)(); 常见错误:常见错误: 忘记了前一个忘记了前一个()(),写成,写成int * p(); / /* *声明一个返回值是整型指针的函声明一个返回值是整型指针的函数数* */ / 忘掉了后一个忘掉了后一个()(),写成,写成 int (*p); / /* *定义了一个整型指针定义了一个

33、整型指针* */ / 定义时后一个括号内的参数类型与指向的函数参数类型不匹配定义时后一个括号内的参数类型与指向的函数参数类型不匹配(2)将一个函数名将一个函数名( (函数入口地址函数入口地址) )赋值给指针变量赋值给指针变量 对函数指针进行定义后,可以为它赋一个函数的入口地址,即只有使它指向对函数指针进行定义后,可以为它赋一个函数的入口地址,即只有使它指向一个函数,才能使用该指针。一个函数,才能使用该指针。 格式:函数指针格式:函数指针= =函数名函数名; ; 注意:注意:函数名后不能带括号和参数。函数名后不能带括号和参数。(3)用指针变量实现函数的调用用指针变量实现函数的调用 格式:格式:(

34、 (* *函数指针函数指针)()(实参列表实参列表););注意注意: 对于函数指针,进行对于函数指针,进行p+i、p+或或p-等运算是没有意义的。等运算是没有意义的。30【案例【案例6.5】 用函数指针变量作参数,求最大值、最小值和两数之和。用函数指针变量作参数,求最大值、最小值和两数之和。int main() int a,b,max(int,int), min(int,int),add(int,int); void process(int,int,int (*fun)(); scanf(%d,%d,&a,&b); process(a,b,max); process(a,b,min); pro

35、cess(a,b,add); return 0;void process(int x,int y,int (*fun)() int result; result=(*fun)(x,y); printf(%dn,result);int max(int x,int y) int min(int x,int y) int add(int x,int y) 6.2 指针与函数指针与函数6.3.1 6.3.1 指针与一维数组指针与一维数组n为什么一个int型指针能够指向一个整型数组?数组名是数组的首地址(地址常量)&a0是整型元素的地址p是整型指针a0的类型和p的基类型相同6.3 指针与数组指针与数组

36、变址运算符Pp+不是增加1字节,取决于p的基类型6.3 指针与数组指针与数组另两种等价的引用形式:因p保存的是数组的首地址所以p也可看成是数组名6.3 指针与数组指针与数组地址恒等式:地址恒等式:a+i=&ai=p+i元素值恒等式:元素值恒等式:*(a+i) = ai =*(p+i)=pi6.3 指针与数组指针与数组 如左图,如左图,p1、p2分别表示指向数组分别表示指向数组data的指的指针变量,则针变量,则p2-p1表示两指针之间所间隔的数表示两指针之间所间隔的数组元素个数,而不是指针的地址之差组元素个数,而不是指针的地址之差。左。左图图中中,p2-p1为为4。 指针的算术运算只包括两个相

37、同类型指针相指针的算术运算只包括两个相同类型指针相减及指针加上或减去一个整数,其他的操作减及指针加上或减去一个整数,其他的操作( (如指针相加、相乘、相除,或指针加上和减去如指针相加、相乘、相除,或指针加上和减去一个浮点数等一个浮点数等) )都是非法的。都是非法的。 两指针之间还可以进行关系运算,如果在同两指针之间还可以进行关系运算,如果在同一个数组一个数组data中,中,p1指向指向datai,p2指向指向dataj,并且,并且ij,则,则p1p2。左图中左图中,表达式,表达式p1p2为真。为真。int main() int i,*p,a7; p=a; for(i=0;i7;i+) scan

38、f(%d,p+); printf(n); for(i=0;i7;i+,p+) printf(%d,*p); return 0;注意指针的当前值注意指针的当前值p=a;pp58762730123456apppppp6.3 指针与数组指针与数组6.3.2 6.3.2 函数参数的多样性函数参数的多样性6.3 指针与数组指针与数组【案例【案例6.7】 利用数组名作为实参与形参,输出数组中各元素的值。利用数组名作为实参与形参,输出数组中各元素的值。#include void data_put( (int str, int n) ) /*数组名数组名str作为函数形参作为函数形参*/ int i; pri

39、ntf( (str的地址是:的地址是:%pn,str) );/*打印打印str的地址的地址*/ for( (i=0; in; i+) ) stri=i; /*用数组下标号为数组元素赋值用数组下标号为数组元素赋值*/int main()() int a6,i; printf( (a的地址是:的地址是:%pn,a) ); /*打印打印a的地址的地址*/ data_put( (a, 6) ); /*数组名数组名a作为函数实参作为函数实参*/ for( (i=0; i6; i+) ) printf( (%2d, ai) ); /*输出数组输出数组a中各元素的值中各元素的值*/ return 0; 本案

40、例中,本案例中,实参实参a和形参和形参str的地址值是相同的,说明将数组作为函数参的地址值是相同的,说明将数组作为函数参数其实是将数组的首地址作为函数参数进行传递,而数组元素本身不被复数其实是将数组的首地址作为函数参数进行传递,而数组元素本身不被复制。制。 当实参当实参a传递给形参传递给形参str时,它们共享了同一个内存空间,该数组在主调时,它们共享了同一个内存空间,该数组在主调函数中是函数中是a,在被调函数的作用域中被称为,在被调函数的作用域中被称为str,它们指的都是同一个数组。,它们指的都是同一个数组。很显然,对很显然,对str的任何变化实质上就是对的任何变化实质上就是对a数组的改变。本

41、案例在数组的改变。本案例在data_put()()函数中将数组函数中将数组str中的各元素赋为下标值,一旦被调函数的空间被释放后,中的各元素赋为下标值,一旦被调函数的空间被释放后,最终得到的最终得到的a数组是变化后的结果,即也被赋值成了下标值。数组是变化后的结果,即也被赋值成了下标值。 需要说明的是,形参数组需要说明的是,形参数组int str不必给出数组长度,不必给出数组长度,即不必即不必写成写成int str6。这是由于传递的是数组首地址而不是数组元素值,编译系统并不关。这是由于传递的是数组首地址而不是数组元素值,编译系统并不关心数组元素的个数等具体细节,数组长度和传递本身无任何关系。心数

42、组元素的个数等具体细节,数组长度和传递本身无任何关系。6.3 指针与数组指针与数组【案例【案例6.7 改造改造1】 利用数组名作为实参,指针变量作为形参,输出数组利用数组名作为实参,指针变量作为形参,输出数组中各元素的值。中各元素的值。#include void data_put( (int * str, int n) ) /*指针变量指针变量str作为函数形参作为函数形参*/ int i; printf( (str的地址是:的地址是:%pn,str) ); for( (i=0; in; i+) ) *( (str+i) )=i; /*此句等价于写成此句等价于写成stri=i;*/ int m

43、ain()() int a6,i; printf( (a的地址是:的地址是:%pn,a) ); data_put( (a, 6) ); /*数组名数组名a作为函数实参作为函数实参*/ for( (i=0; i6; i+) ) printf( (%2d, ai) ); return 0;6.3 指针与数组指针与数组【案例【案例6.7 改造改造2】 利用指针变量作为实参,数组名作为形参,输出数利用指针变量作为实参,数组名作为形参,输出数组中各元素的值。组中各元素的值。#include void data_put( (int str, int n) ) /*数组名数组名str作为函数形参作为函数形参

44、*/ int i; printf( (str的地址是:的地址是:%pn,str) ); for( (i=0; in; i+) ) stri=i; int main()() int a6,i,* p=a; /*定义指针变量定义指针变量p并指向数组并指向数组a*/ printf( (a的地址是:的地址是:%pn,a) ); data_put( (p, 6) ); /*指针变量指针变量p作为函数实参作为函数实参*/ for( (i=0; i6; i+) ) printf( (%2d, *( (p+i); /*“*( (p+i) )”等价于写成等价于写成pi*/ return 0;6.3 指针与数组指

45、针与数组【案例【案例6.7 改造改造3】 利用指针变量作为实参与形参,输出数组中各元素的值利用指针变量作为实参与形参,输出数组中各元素的值。#include void data_put( (int * str, int n) ) /*指针变量指针变量str作为函数形参作为函数形参*/ int i; printf( (str的地址是:的地址是:%pn,str) ); for( (i=0; in; i+) ) *( (str+i) )=i; /*此句等价于写成此句等价于写成stri=i;*/ int main()() int a6,i,* p=a; /*定义指针变量定义指针变量p并指向数组并指向数

46、组a*/ printf( (a的地址是:的地址是:%pn,a) ); data_put( (p, 6) ); /*指针变量指针变量p作为函数实参作为函数实参*/ for( (i=0; i6; i+) ) printf( (%2d, *( (p+i); /*“*( (p+i) )”等价于写成等价于写成pi*/ return 0;6.3 指针与数组指针与数组6.3.3 6.3.3 指针和字符串指针和字符串6.3 指针与数组指针与数组 在在C语言中没有单独的字符串类型。字符串是以字符数组的形式存储的,在最后有一个语言中没有单独的字符串类型。字符串是以字符数组的形式存储的,在最后有一个“0”的标志。由

47、于数组可以使用指针进行访问,因此字符串也可以用指针进行访问。的标志。由于数组可以使用指针进行访问,因此字符串也可以用指针进行访问。 系统在存储一个字符串常量时首先要给出一个起始地址,从该地址开始连续存放字符串系统在存储一个字符串常量时首先要给出一个起始地址,从该地址开始连续存放字符串中的各字符。那么,该起始地址就代表了字符串首字符的存储位置。由此可见,字符串常中的各字符。那么,该起始地址就代表了字符串首字符的存储位置。由此可见,字符串常量实质上是一个指向该字符串首字符的指针常量。量实质上是一个指向该字符串首字符的指针常量。 例如,字符串例如,字符串“China”的值本身就是一个地址,从它指定的

48、存储单元开始连续存放往的值本身就是一个地址,从它指定的存储单元开始连续存放往后的后的6个字符。个字符。 如果定义一个字符指针接收字符串常量的值,则该指针就指向字符串的首地址。这样,如果定义一个字符指针接收字符串常量的值,则该指针就指向字符串的首地址。这样,就产生就产生另一种更为方便灵活的处理字符串的办法另一种更为方便灵活的处理字符串的办法利用字符指针:利用字符指针: char * sp; /*定义字符指针变量定义字符指针变量sp*/ 和普通的指针一样,定义字符指针后,也必须让它有所指和普通的指针一样,定义字符指针后,也必须让它有所指向:向: sp=China; /*将字符串的首元素地址赋给将字

49、符串的首元素地址赋给sp,注意不是将整个字符串赋给,注意不是将整个字符串赋给sp*/ 这两句可以合并为:这两句可以合并为:char sp=China; 若要进行打印输出,可使用:若要进行打印输出,可使用: printf(%s,sp); /*打印字符串打印字符串“China”*/ printf(%s,sp+2); /*指针指针sp移动到字符移动到字符i处,打印子串处,打印子串ina*/由于字符串保存在只读的常量存储区中,我们不能对由于字符串保存在只读的常量存储区中,我们不能对sp所指向的存所指向的存储单元进行写操作。下面的语句是非法的:储单元进行写操作。下面的语句是非法的:*sp=Z;但是,如果

50、将字符串但是,如果将字符串China存储在数组中,然后再用一个字符指存储在数组中,然后再用一个字符指针进行指向,例如:针进行指向,例如:char sa10= China;char * sp=sa;那么,此时那么,此时sp指向该字符串指向该字符串China。因为数组名是一个地址常量。因为数组名是一个地址常量,所以,所以sa的值不可修改,但指针的值不可修改,但指针sp的值的值( (sp的指向的指向) )可以被修改,可以被修改,sp所指所指向的字符串也可以被修改。向的字符串也可以被修改。 例如,要将例如,要将sp所指向的字符串的第一个字符修改为所指向的字符串的第一个字符修改为Z,可使用:,可使用:

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 初中资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁