《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
3、.5,d=8.6; char e=x,f=y;,变量地址(变量指针)是由操作系统自动分配,8,8.2 变量的指针和指向变量的指针变量,9,指针与指针变量,程序中通过地址或变量访问存储单元的方法称为直接访问。 把一个变量的地址存储到另一个变量中,而通过另一个变量来访问该变量的方法称为间接访问。专门存放指针(地址)的变量称为指针(地址)变量。,10,11,变量的指针就是变量的地址。存放变量地址的变量则是指针变量,用来指向另一个变量。为了表示指针变量和它所指向的变量之间的联系,在程序中用“*”符号表示“指向”,例如,i_pointer代表指针变量,而*i_pointer是i_pointer所指向的变
4、量,见图。,12,可以看到,*i_pointer也代表一个变量,它和变量i是同一回事。下面两个语句作用相同: i=3; *i_pointer=3; 第个语句的含意是将3赋给指针变量i_pointer所指向的变量。,13,8.2.1 定义指针变量,C语言规定所有变量在使用前必须定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是用来专门存放地址的。必须将它定义为“指针类型”。 C语言中指针变量也有类型。指针变量的类型是指指针变量指向对象的类型,即指针变量存储的指针所表示的对象的类型。 指针变量定义格式: 类型标识符 * 指针变量名;,14,定义格式中类型标识符规定指
5、针变量的类型。 如:int * pa; 指针变量pa是一个整型指针变量,它指向一个整型变量。即pa可用来存放一个整型变量的地址(指针)。,15,float *pb,b; /*定义实型指针变量pb和实型变量b*/ pb= /*将实型变量b的地址赋值给指针变量pb,这样指针变量pb就指向了实型变量b,通过pb可以间接对b进行访问*/,16,8.2.2 指针变量的引用,指针即地址,它实际上是整型数,因此指针变量有时可以象整型数一样进行引用。 char *pc; printf(“%x“,pc); /*输出指针变量的值*/ 但请牢记,指针变量中只能存放地址(指针),不要将一个整型量(或任何其他非地址类型
6、的数据)赋给一个指针变量。下面的赋值是不合法的: pointer_1=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
7、;b=10; pointer_1= ,运行,20,main() int *p1,*p2,i1,i2; scanf(“%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指向变量赋值*/ (*poi
8、nter_1)+相当于a+。注意括号是必要的,如果没有括号,就成为了*pointer_1+,和*为同一优先级别,而结合方向为自右而左,因此它相当于*(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; s
9、ub( ,x,y,&x,&y,px,py,函数调用时的值传递,运行,26,main() void swap1(int *p1,int *p2); int a=3,b=5; printf(“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
10、 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; printf(“a=%d,b=%dn“,a,b); swap3( ,3,5,a,b,&a,&b,p1,p2,函数调用时的值传递,交换,运行,29,8.3 通过指针引用数组,30,数组名代表数组的首地址,这个地址是在程序编译时由编译程序确定,程序运行时由操作系统具体分配。 数组名因此也是一个指针。但它不是一个指针变量,因此其值是不能通过程序修改的。 不同维数的数组名表示不同级别的指
11、针。一维数组名是一级指针,二维数组名是二级指针,以此类推。,31,如:int a5=1,2,3,4,5,*pa,i; a=*/,a,a,pa,32,指针的加减运算含义,指针可作加减运算。 C语言规定:指针加一表示其指向的下一对象的地址,减一表示其指向的上一对象的地址。 为实现该规定,C在处理有关指针的加碱运算时,会进行 特别处理。 如指针变量p,加上n实际加的数值是: nsizeof(p指向的对象类型),33,a,a,pa,数组元素ai的实际地址可用公式:a+i2计算。 类似pa+n实际是:pa+n2。即pa指向对象之后的第n个对象的地址。,34,数组元素访问的方法,下标法:如 a0,ai 地
12、址法:如 *(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(“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); ,下标法,地址法,指针法
13、,运行,访问数组元素的几种方法,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,fib20=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
14、(“%12d“,*pf); ,fib,pf,运行,利用数组计算斐波拉齐数列,38,用数组名作函数参数,用数组名也可作函数的实参或形参,可将一个数组的首地址传递到另一个函数中。从而在函数中,通过该地址对作用域不在本函数的数组进行操作。这是因为,实参数组和形参数组共占同一段内存单元。 如: main( ) void f (int arr ,int n) int array10; arr0=0; f(array,10); array为实参数组名,arr为形参数组名。,39,array,array,arr,函数调用时,数组array的首地址传递给形参数组arr(变量),在被调用函数中借助arr可以操作
15、数组array,所谓形参数组arr实际和数组array是同一内存位置。,40,形参数组,实参数组名代表该数组首地址。而形参是用来接收从实参传递过来的数组首地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组作为指针变量来处理的。 如: void fun(int a 10); 等价于:void fun(int *a); 因此形参数组的长度在定义时可以省略。 float fun(char str ); 等价于:float fun(char *str); 常用这种方法通过调用一个函数来改变实参数组的值。,41,需要说明的是:C语言调用函数时虚实结合的方法都是
16、采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组起始地址,因此传递的值是数组首地址,所以要求形参为指针变量。,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=xj;xj=temp; return; main() int i,a10=3,7,9,11,0,6,7,5,4,2; printf(“The original ar
17、ray: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,二维数组指针表示*,和一维数组一样,二维数组名也表示数组的首地址。 二维数组名表示二级指针。 对于二维数组,可以理解为由一维数组作数组元素组成的一维数组。,46,如:int a34;,a,“一维数组”a有3个元素,每个元素又是由有4个元素的一维数组组成
18、,47,a,a+0 表示 &a0 *a 表示 a0 a+1 表示 &a1 *(a+1) 表示 a1 a+2 表示 &a2 *(a+2) 表示 a2,二级指针指向行,一级指针指向元素列,a+1,a+2,a0+0 表示 &a00 *a0或*a 表示 a00 a0+1 表示 &a01 *(a0+1)或*(*(a)+1) 表示 a01 a1+2 表示 &a12 *(a1+2)或*(*(a+1)+2)表示a12 a2+3 表示 &a23 *(a2+3)或*(*(a+2)+3)表示a23,一级指针指向元素列,数组元素,48,a,a+1,a+2,二维数组名a代表二维数组的首地址,它是二级指针,它在数值上等于
19、&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,main( ) static int a35=1,2,3,
20、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。 二维数组指针定义形式:
21、 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“);
22、 若p定义为: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=st
23、r;*ps!=0;ps+) putchar(*ps);/*逐个字符输出*/ ps=str; gets(ps);/*通过ps输入字符串到数组str*/ scanf(“%s“,ps);,str,&str0,ps,55,# include “stdio.h“ main( ) 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
24、指向该字符串。不是用字符串去初始化p。*/ char *ps; ps=“China“;/*把字符串常量的存储地址赋值给字符变量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 ,运行,函数实
25、现字符串拷贝,59,s1,s2,返回-1,60,8.5 指向函数的指针,61,函数指针,函数指针即指向函数的指针。函数在运行时,是存储在内存中。函数存储的首地址被作为函数地址。程序中的函数名和数组名类似,它代表的就是函数地址。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。 函数名和数组名一样虽然是指针,但不是变量,因此不能修改。函数指针变量的定义格式: 类型表示符 (*指针变量名)( ); 如:int (*p)(); 定义一个函数指针变量p,该变量可用来指向一个整型函数。 double *(*pa)(); char *(*pb)();,62,int fun(int x,int t)
26、; int (* pf)( ); pf=fun;/*函数指针变量的赋值*/ (*p)(a,b);/*通过函数指针变量调用函数*/ p(a,b);/*通过函数指针变量调用函数*/,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); /*或写
27、作total2=pt(p,12);*/ printf(“total1=%dntotal2=%dn“,total1,total2); arr_add(int arr ,int n) int i,sum=0; for(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
28、 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)(int,int) int reSult; result=(*fun)(x,y); printf(“%Dn“,result); ,利用函数指针调用不同功能函数,65,8.6 返回指针值的函数,66,一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是
29、指针类型而已。 这种带回指针值的函数,一般定义形式为: 类型名* 函数名(参数表) 函数体 ,67,char * strchr(char *str,char ch) while (*str!=0) if (*str=ch) return (str); else str+; return (0); main() char *strchr(char *str,char ch); char *pt,ch,line =“I love China“; ch=I; pt=strchr(line,ch); printf(“nString starts at address %o.n“,line); prin
30、tf(“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;p+) ; do *p+=*str2+; while (*str2!=0); *p=0; return(str1); ,0,J,I,H,G,for循环退出,F,do循环退出,执行赋值语句,再返回,
31、69,8.7 指针数组和多重指针,70,指针数组是指存储指针的数组。 如:int *p5; p是一个含5个元素的数组,其数组元素是整型指针。所以p叫整型指针数组。,71,# include “stdio.h“ main() char * str3=“China“,“U.S.A“,“Canada“; int i; for (i=0;i3;i+) printf(“%sn“,stri); ,str,str是字符指针数组,其3个元素分别指向3个字符串,运行,72,数组指针和指针数组的区别: 数组指针是指向数组的指针,而指针数组是由指针作数组元素的数组。 int (*p1)10;/*数组指针*/ int
32、 *p210;/*指针数组*/,73,指向指针的指针,指针变量本身是一个变量,因此其也有地址。其地址可以存储到另一个指针变量中,该变量就叫指向指针的指针变量。 按此逻辑可以定义多重指针变量。 基本类型变量的地址称为一级指针,指向基本变量的指针变量就叫一级指针变量,它的地址则是二级指针变量,指向一级指针变量的指针变量叫二级指针变量。以此类推。,74,int *ppa,*pa,a; /*ppa是一个指向整型指针变量 的指针变量*/ pa= /*给ppa指向的对象所指向的对 象(a)赋值*/,75,main() int *p1,*p2,*p3,*p4,i; i=3; p1= ,若:p1=&i 或 p
33、1=&p3 都是错误的,因为赋值号两边指针级别不一致。,运行,76,其它类型指针,C语言的指针类型非常丰富,其定义很灵活。 如:double *(*p)105; p是一个指针变量,其指向的对象是一个二维数组,该数组的所有元素都是double类型的指针。即p是一个double类型二维指针数组指针。,77,指针定义及确定方法:按*、( )、 的运算优先级来确定。 如:char *(*p)2;,78,主函数的参数*,main函数也可定义形式参数,但其形参的定义有固定形式,不能任意定义。 main函数形参定义格式: main(int argc,char *argv ) 主函数通过操作系统调用,因此其实
34、参由命令行给出。命令行的一般形式: 命令名 参数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是形参数组名,实际是指针变量, 因此可作自加运算*/ prin
35、tf(“%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提供的动态分配函数可以实现在程序中
36、动态的分配和释放存储空间。 使用这些函数时,程序中要包含头文件“stdlib.h”或“alloc.h”。 在程序中动态开辟的存储空间,在使用完毕后要释放,否则会始终占据存储空间。,85,指向void 类型的指针 void * p; p叫作空指针,是指p指向的对象为void,void类型是C语言的一种特殊类型。空指针在使用时必需先进行强制运算。C动态函数的返回值均为该类型指针。,86,一、malloc函数,函数声明格式(函数原型) void * malloc(unsigned int size); 功能:在内存开辟指定大小的存储空间,并返回该存储空间的首地址,若开辟失败则返回0。 说明:存储空间
37、大小由size指定,单位为字节。函数返回的是void指针,使用前要作强制运算。,87,二、free函数,函数声明格式(函数原型) void free(void * ptr); 功能:释放ptr指向的存储空间。 说明:该函数主要释放由malloc、calloc、realloc等函数开辟的存储空间。,88,# include “stdlib.h“ # include “stdio.h“ main() 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,
链接地址:https://www.31doc.com/p-2143235.html