C语言第8章的课件.ppt

上传人:sccc 文档编号:5382158 上传时间:2023-07-01 格式:PPT 页数:91 大小:618.01KB
返回 下载 相关 举报
C语言第8章的课件.ppt_第1页
第1页 / 共91页
C语言第8章的课件.ppt_第2页
第2页 / 共91页
C语言第8章的课件.ppt_第3页
第3页 / 共91页
C语言第8章的课件.ppt_第4页
第4页 / 共91页
C语言第8章的课件.ppt_第5页
第5页 / 共91页
点击查看更多>>
资源描述

《C语言第8章的课件.ppt》由会员分享,可在线阅读,更多相关《C语言第8章的课件.ppt(91页珍藏版)》请在三一办公上搜索。

1、1,第8章 指针,2,本章要求,掌握地址与指针的概念;掌握各类型指针变量的定义、赋值、指针运算符、指针运算;掌握一维数组的指针、二维数组的指针、字符串的指针的定义与应用;了解函数的指针和指向函数的指针变量;了解指针数组的概念与定义,多级指针的概念与定义。,3,8.1 地址和指针的概念,4,指针是C语言的一个重要概念,也是C语言的一个特色。它可以有效地表示复杂的数据结构;动态分配内存;能方便地使用字符串;有效而方便地使用数组;在函数调用时能返回得到多于一个的值;能直接处理内存地址等。掌握指针的应用,可以使程序简洁、紧凑、高效。每一个学习和使用C语言的人,都应当深入地学习和掌握指针。可以说,不掌握

2、指针就是没有掌握C的精华。,5,程序中每个实体,如变量、数组、函数等都要在内存中占有一个可标识的存储区域。每个存储区域由若干个字节的存储单元组成。内存中每个字节的存储单元都有一个“地址”。一个存储区域的“地址”是指该区域中第一个字节的地址。指针实际上是对存储单元地址的一种形象化描述。指针即地址。,6,程序中的变量表示命名了的存储区域。不同类型变量其存储区域字节单元数不一样。变量名表示该存储区域的别名。变量值是该存储区域中存储的数据。变量地址是该存储区域的首地址。变量地址可通过对变量名进行取地址运算得到。如:&a得到变量a的地址。,7,int a=3,b=4;float c=4.5,d=8.6;

3、char e=x,f=y;,变量地址(变量指针)是由操作系统自动分配,8,8.2 变量的指针和指向变量的指针变量,9,指针与指针变量,程序中通过地址或变量访问存储单元的方法称为直接访问。把一个变量的地址存储到另一个变量中,而通过另一个变量来访问该变量的方法称为间接访问。专门存放指针(地址)的变量称为指针(地址)变量。,10,11,变量的指针就是变量的地址。存放变量地址的变量则是指针变量,用来指向另一个变量。为了表示指针变量和它所指向的变量之间的联系,在程序中用“*”符号表示“指向”,例如,i_pointer代表指针变量,而*i_pointer是i_pointer所指向的变量,见图。,12,可以

4、看到,*i_pointer也代表一个变量,它和变量i是同一回事。下面两个语句作用相同:i=3;*i_pointer=3;第个语句的含意是将3赋给指针变量i_pointer所指向的变量。,13,8.2.1 定义指针变量,C语言规定所有变量在使用前必须定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是用来专门存放地址的。必须将它定义为“指针类型”。C语言中指针变量也有类型。指针变量的类型是指指针变量指向对象的类型,即指针变量存储的指针所表示的对象的类型。指针变量定义格式:类型标识符*指针变量名;,14,定义格式中类型标识符规定指针变量的类型。如:int*pa;指针变

5、量pa是一个整型指针变量,它指向一个整型变量。即pa可用来存放一个整型变量的地址(指针)。,15,float*pb,b;/*定义实型指针变量pb和实型变量b*/pb=/*将实型变量b的地址赋值给指针变量pb,这样指针变量pb就指向了实型变量b,通过pb可以间接对b进行访问*/,16,8.2.2 指针变量的引用,指针即地址,它实际上是整型数,因此指针变量有时可以象整型数一样进行引用。char*pc;printf(%x,pc);/*输出指针变量的值*/但请牢记,指针变量中只能存放地址(指针),不要将一个整型量(或任何其他非地址类型的数据)赋给一个指针变量。下面的赋值是不合法的:pointer_1=

6、100;(pointer_1为指针变量,100为整数),17,有关指针的运算符,&取地址运算符。单目运算符,作用是取操作数地址。*指针运算符。单目运算符,作用是对操作数作指向运算。其操作数必需是一个指针。如:*p=1表达式表示根据变量p的值,找到其对应的存储单元,给该单元赋值1。*(&a)表达式等价于a,18,int*pa,a;pa=,输出pa指向对象的值(间接访问),直接访问,19,例通过指针变量访问整型变量。main()int a,b;int*pointer_1,*pointer_2;a=100;b=10;pointer_1=,运行,20,main()int*p1,*p2,i1,i2;sc

7、anf(%d,%d,运行,几个指针应用的例子,21,main()int*p1,*p2,*p,i1=10,i2=20;p1=,运行,22,main()int*p1,*p2,i1,i2,i;i1=10;i2=20;p1=,运行,23,注意,*p1表达式表示指针变量p1指向的对象。在访问指针变量指向对象前,指针变量必需已指向明确。如:float*pa,a;*pa=3.14;/*错误,因为pa尚未指向任何变量*/pa=/*给pa指向变量赋值*/(*pointer_1)+相当于a+。注意括号是必要的,如果没有括号,就成为了*pointer_1+,和*为同一优先级别,而结合方向为自右而左,因此它相当于*(

8、pointer_1+)。由于+在pointer_1的右侧,是“后加”,因此先对pointer_1的原值进行*运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。,24,8.2.3 用指针作为函数参数,用指针作函数参数,可将一个变量的地址传递到另一个函数中。从而在函数中,通过变量地址对作用域不在本函数的变量进行操作。,25,main()void sub(int*px,int*py);int x,y;sub(,x,y,&x,&y,px,py,函数调用时的值传递,运行,26,main()void swap1(int*p1,int*p2);int a=3,b=5;p

9、rintf(a=%d,b=%dn,a,b);swap1(,3,5,a,b,&a,&b,p1,p2,函数调用时的值传递,交换,运行,27,main()void swap2(int p1,int p2);int a=3,b=5;printf(a=%d,b=%dn,a,b);swap2(a,b);printf(a=%d,b=%dn,a,b);void swap2(int p1,int p2)int t;t=p1;p1=p2;p2=t;,3,5,a,b,3,5,p1,p2,函数调用时的值传递,交换,运行,28,main()void swap3(int*p1,int*p2);int a=3,b=5;pr

10、intf(a=%d,b=%dn,a,b);swap3(,3,5,a,b,&a,&b,p1,p2,函数调用时的值传递,交换,运行,29,8.3 通过指针引用数组,30,数组名代表数组的首地址,这个地址是在程序编译时由编译程序确定,程序运行时由操作系统具体分配。数组名因此也是一个指针。但它不是一个指针变量,因此其值是不能通过程序修改的。不同维数的数组名表示不同级别的指针。一维数组名是一级指针,二维数组名是二级指针,以此类推。,31,如:int a5=1,2,3,4,5,*pa,i;a=*/,a,a,pa,32,指针的加减运算含义,指针可作加减运算。C语言规定:指针加一表示其指向的下一对象的地址,减

11、一表示其指向的上一对象的地址。为实现该规定,C在处理有关指针的加碱运算时,会进行 特别处理。如指针变量p,加上n实际加的数值是:nsizeof(p指向的对象类型),33,a,a,pa,数组元素ai的实际地址可用公式:a+i2计算。类似pa+n实际是:pa+n2。即pa指向对象之后的第n个对象的地址。,34,数组元素访问的方法,下标法:如 a0,ai地址法:如*(a),*(a+i),35,main()int a5=1,3,5,7,9,i,*p;for(i=0;i5;i+)printf(%4d,ai);printf(n);for(i=0;i5;i+)printf(%4d,*(a+i);printf

12、(n);for(p=a;pa+5;p+)printf(%4d,*p);printf(n);for(p=a,i=0;i5;i+)printf(%4d,*(p+i);printf(n);for(p=a,i=0;i5;i+)printf(%4d,pi);,下标法,地址法,指针法,运行,访问数组元素的几种方法,36,int a10,*p,i;for(i=0;i10;i+)scanf(%d,a+);/*错误,数组名a不是指针变量,不能作自加运算,a+=1也是*/for(i=0;i10;i+)scanf(%d,a+i);/*正确,a+i表示ai的地址,即/*正确*/,37,main()int i,fib2

13、0=1,1,*pf;for(pf=fib+2;pffib+20;pf+)*pf=*(pf-1)+*(pf-2);for(pf=fib,i=0;pffib+20;pf+,i+)if(i%5=0)printf(n);printf(%12d,*pf);,fib,pf,运行,利用数组计算斐波拉齐数列,38,用数组名作函数参数,用数组名也可作函数的实参或形参,可将一个数组的首地址传递到另一个函数中。从而在函数中,通过该地址对作用域不在本函数的数组进行操作。这是因为,实参数组和形参数组共占同一段内存单元。如:main()void f(int arr,int n)int array10;arr0=0;f(a

14、rray,10);array为实参数组名,arr为形参数组名。,39,array,array,arr,函数调用时,数组array的首地址传递给形参数组arr(变量),在被调用函数中借助arr可以操作数组array,所谓形参数组arr实际和数组array是同一内存位置。,40,形参数组,实参数组名代表该数组首地址。而形参是用来接收从实参传递过来的数组首地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组作为指针变量来处理的。如:void fun(int a 10);等价于:void fun(int*a);因此形参数组的长度在定义时可以省略。float f

15、un(char str);等价于:float fun(char*str);常用这种方法通过调用一个函数来改变实参数组的值。,41,需要说明的是:C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组起始地址,因此传递的值是数组首地址,所以要求形参为指针变量。,42,数组名作参数的几种情况,43,例将数组a中n个整数按相反顺序存放。void inv(int x,int n)/*形参x是数组名*/int temp,i,j,m=(n-1)/2;for(i=0;i=m;i+)j=n-1-i;temp=xi;xi=

16、xj;xj=temp;return;main()int i,a10=3,7,9,11,0,6,7,5,4,2;printf(The original array:n);for(i=0;i10;i+)printf(%d,ai);printf(n);inv(a,10);printf(The array has been inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,运行,44,最后数组a的内容,45,二维数组指针表示*,和一维数组一样,二维数组名也表示数组的首地址。二维数组名表示二级指针。对于二维数组,可以理解为由一维数组作数组元素组成的一

17、维数组。,46,如:int a34;,a,“一维数组”a有3个元素,每个元素又是由有4个元素的一维数组组成,47,a,a+0 表示&a0*a 表示 a0a+1 表示&a1*(a+1)表示 a1a+2 表示&a2*(a+2)表示 a2,二级指针指向行,一级指针指向元素列,a+1,a+2,a0+0 表示&a00*a0或*a 表示 a00a0+1 表示&a01*(a0+1)或*(*(a)+1)表示 a01a1+2 表示&a12*(a1+2)或*(*(a+1)+2)表示a12a2+3 表示&a23*(a2+3)或*(*(a+2)+3)表示a23,一级指针指向元素列,数组元素,48,a,a+1,a+2,

18、二维数组名a代表二维数组的首地址,它是二级指针,它在数值上等于&a00,不过两者级别不一样类型也不一样。a指向一个含4个整型数的一维数组(指向行),因此a+1指向下一行数组。如上示意图,若数组首地址a是1000,则a+1的值是1008、a+2的值是1016。a0代表行数组名,它在数值上不仅等于&a00,且两者级别类型都一致,指向列。因此,a0+1的值是1002、a0+2的值是1004、a1+3的值是1012、a2+1的值是1018。,1000,1008,1016,1000,1000,1002,1004,1006,1016,1018,1020,1022,a0 a0+1 a0+2 a0+3,49,

19、main()static int a35=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;int*p;for(p=a0;pa0+15;p+)printf(%4d,*p);printf(n);p=a0可写作:p=*a或p=&a00。但不能写作:p=a或p=&a0。因为赋值号两端的指针不是同级别。a,&a0指向一个含5个整型数的数组,叫数组指针。,运行,50,数组指针,数组指针即指向数组的指针,一维数组指针定义形式:int(*pa)5;pa被定义为一个一维数组指针,它指向的对象是一个含5个整型元素的数组。因此pa+1指向下一数组,即表达式pa+1的值实际上等于pa+5*2。

20、二维数组指针定义形式:int(*pa)23;pa被定义为一个二维数组指针,它指向的对象是一个含2行3列的共6个元素的数组。因此pa+1指向下一数组,即表达式pa+1的值实际上等于pa+2*3*2。,51,main()static int a35=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;int i,j,(*p)5;p=a;/*数组名a和数组指针变量p,两者类型及级别一致*/for(i=0;i3;i+)for(j=0;j5;j+)printf(%4d,*(*(p+i)+j);/*(*(p+i)+j)表达式可写作*(pi+j)或pij*/printf(n);若p定义为

21、:int(*p)10;则:p=a表达式是错误的,尽管p和a都是二级指针,但二者指向的对象不同。表达式p+1和a+1的值也就不一样。,运行,52,8.4 通过指针引用字符串,53,字符串存储在字符数组中,可用一字符指针指向该数组,从而通过该字符指针可以操作字符串。在C程序中,可以用两种方法访问一个字符串。用字符数组存放一个字符串。用字符指针指向一个字符串。,54,如:char str=China,*ps;ps=str;/*数组首地址赋值给字符指针变量ps*/puts(ps);/*通过ps输出字符串的几种方法*/printf(%s,ps);for(ps=str;*ps!=0;ps+)putchar

22、(*ps);/*逐个字符输出*/ps=str;gets(ps);/*通过ps输入字符串到数组str*/scanf(%s,ps);,str,&str0,ps,55,#include stdio.hmain()char str20,*p;p=str;gets(p);/*输入字符串*/for(;*p!=0;p+)if(*p=A*/,p,str,运行,56,在C程序中,允许用字符串设置字符指针变量:如:char*p=China;/*该定义表示定义字符指针变量p,并用字符串常量China的存储地址初始化p。即p指向该字符串。不是用字符串去初始化p。*/char*ps;ps=China;/*把字符串常量的

23、存储地址赋值给字符变量ps,不是把字符串赋值给ps*/,57,注意,char*ps;gets(ps);或 scanf(“%s”,ps);上面的写法错误,因为ps指针并未指向任何可用于存储字符串的空间。正确的写法:char*ps,str100;ps=str;gets(ps);,58,int strcomp(char*str1,char*str2)/*可写为int strcomp(char str1,char str2)*/while(*str1!=0,运行,函数实现字符串拷贝,59,s1,s2,返回-1,60,8.5 指向函数的指针,61,函数指针,函数指针即指向函数的指针。函数在运行时,是存储

24、在内存中。函数存储的首地址被作为函数地址。程序中的函数名和数组名类似,它代表的就是函数地址。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。函数名和数组名一样虽然是指针,但不是变量,因此不能修改。函数指针变量的定义格式:类型表示符(*指针变量名)();如:int(*p)();定义一个函数指针变量p,该变量可用来指向一个整型函数。double*(*pa)();char*(*pb)();,62,int fun(int x,int t);int(*pf)();pf=fun;/*函数指针变量的赋值*/(*p)(a,b);/*通过函数指针变量调用函数*/p(a,b);/*通过函数指针变量调用函

25、数*/,63,main()int arr_add(int arr,int n);int a34=1,3,5,7,9,11,13,15,17,19,21,23;int*p,total1,total2;int(*pt)();/*定义整型函数指针变量*/pt=arr_add;/*pt指向函数arr_add*/p=a0;total1=arr_add(p,12);total2=(*pt)(p,12);/*或写作total2=pt(p,12);*/printf(total1=%dntotal2=%dn,total1,total2);arr_add(int arr,int n)int i,sum=0;for

26、(i=0;in;i+)sum+=arri;return(sum);,运行,64,main()int max(int,int);/*函数声明*/int min(int,int);/*函数声明*/int add(int,int);/*函数声明*/int a,b;printf(enter a and b:);scanf(%d,%d,,min(int x,int y)/*函数定义*/int Z;if(xy)Z=x;else Z=y;return(Z);add(int x,int y)/*函数定义*/int Z;Z=x+y;return(Z);process(int x,int y,int*fun)(i

27、nt,int)int reSult;result=(*fun)(x,y);printf(%Dn,result);,利用函数指针调用不同功能函数,65,8.6 返回指针值的函数,66,一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。这种带回指针值的函数,一般定义形式为:类型名*函数名(参数表)函数体,67,char*strchr(char*str,char ch)while(*str!=0)if(*str=ch)return(str);else str+;return(0);main()char*strchr(ch

28、ar*str,char ch);char*pt,ch,line=I love China;ch=I;pt=strchr(line,ch);printf(nString starts at address%o.n,line);printf(First occurrence of char%c is address%o.n,ch,pt);printf(This is position%d(starting from 0)n,pt-line);,运行,函数返回指定字符在字符串中的位置,68,char*strcat(char*str1,char*str2)char*p;for(p=str1;*p!=0

29、;p+);do*p+=*str2+;while(*str2!=0);*p=0;return(str1);,0,J,I,H,G,for循环退出,F,do循环退出,执行赋值语句,再返回,69,8.7 指针数组和多重指针,70,指针数组是指存储指针的数组。如:int*p5;p是一个含5个元素的数组,其数组元素是整型指针。所以p叫整型指针数组。,71,#include stdio.hmain()char*str3=China,U.S.A,Canada;int i;for(i=0;i3;i+)printf(%sn,stri);,str,str是字符指针数组,其3个元素分别指向3个字符串,运行,72,数组

30、指针和指针数组的区别:数组指针是指向数组的指针,而指针数组是由指针作数组元素的数组。int(*p1)10;/*数组指针*/int*p210;/*指针数组*/,73,指向指针的指针,指针变量本身是一个变量,因此其也有地址。其地址可以存储到另一个指针变量中,该变量就叫指向指针的指针变量。按此逻辑可以定义多重指针变量。基本类型变量的地址称为一级指针,指向基本变量的指针变量就叫一级指针变量,它的地址则是二级指针变量,指向一级指针变量的指针变量叫二级指针变量。以此类推。,74,int*ppa,*pa,a;/*ppa是一个指向整型指针变量 的指针变量*/pa=/*给ppa指向的对象所指向的对 象(a)赋值

31、*/,75,main()int*p1,*p2,*p3,*p4,i;i=3;p1=,若:p1=&i 或 p1=&p3 都是错误的,因为赋值号两边指针级别不一致。,运行,76,其它类型指针,C语言的指针类型非常丰富,其定义很灵活。如:double*(*p)105;p是一个指针变量,其指向的对象是一个二维数组,该数组的所有元素都是double类型的指针。即p是一个double类型二维指针数组指针。,77,指针定义及确定方法:按*、()、的运算优先级来确定。如:char*(*p)2;,78,主函数的参数*,main函数也可定义形式参数,但其形参的定义有固定形式,不能任意定义。main函数形参定义格式:

32、main(int argc,char*argv)主函数通过操作系统调用,因此其实参由命令行给出。命令行的一般形式:命令名 参数1 参数2.参数n,79,程序执行时,argc的值为命令行中字符串的个数(字符串由空格、跳格分隔)。形参字符指针数组argv的各元素分别指向命令行中的各字符串。如:假设连接后的可执行程序名为cfile.exe。在DOS提示符下执行该程序,键入:cfile Computer 则argc的值为2,argv0,argv1分别指向字符串“cfile”和“Computer”。,80,main(int argc,char*argv)while(argc1)+argv;/*argv是

33、形参数组名,实际是指针变量,因此可作自加运算*/printf(%sn,*argv);-argc;,d:tcli6-19 Computer and C,运行,81,指针数据小结,常用指针类型变量归纳。指针运算。指针即地址,它实际上是整型数。指针能进行的运算。加减整型数。指针进行关系比较。指针两个相减。赋值运算。,82,p1,p2,p1p2不成立p1!=p2成立p1+5=p2成立p2-p1=5成立,83,8.8 动态内存分配与指向它的指针,84,为提高存储器的利用率,有时在程序中需要动态的使用内存,如定义动态数组,它根据用户的要求动态分配内存。在程序运行期间,内存资源可以动态的申请和释放。C提供的

34、动态分配函数可以实现在程序中动态的分配和释放存储空间。使用这些函数时,程序中要包含头文件“stdlib.h”或“alloc.h”。在程序中动态开辟的存储空间,在使用完毕后要释放,否则会始终占据存储空间。,85,指向void 类型的指针 void*p;p叫作空指针,是指p指向的对象为void,void类型是C语言的一种特殊类型。空指针在使用时必需先进行强制运算。C动态函数的返回值均为该类型指针。,86,一、malloc函数,函数声明格式(函数原型)void*malloc(unsigned int size);功能:在内存开辟指定大小的存储空间,并返回该存储空间的首地址,若开辟失败则返回0。说明:

35、存储空间大小由size指定,单位为字节。函数返回的是void指针,使用前要作强制运算。,87,二、free函数,函数声明格式(函数原型)void free(void*ptr);功能:释放ptr指向的存储空间。说明:该函数主要释放由malloc、calloc、realloc等函数开辟的存储空间。,88,#include stdlib.h#include stdio.hmain()int i,n;float*p1,*p2,score;printf(Enter the number of student:);scanf(%d,/*释放p1指向的存储空间*/,运行,89,三、calloc函数,函数声明格式(函数原型)void*calloc(unsigned int num,unsigned int size);功能:在内存开辟num个大小为size个字节的存储空间,并返回该存储空间的首地址,若开辟失败则返回0。,90,四、realloc函数,函数声明格式(函数原型)void*realloc(void*ptr,unsigned int size);功能:将ptr指向的存储空间大小重新改为size个字节,若开辟失败则返回0。,91,本章作业,P246-247 3、4、8,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号