第7章指针许建龙xujianlong126@126.com2020/3/52内容提要指针的概念;–难点:对指针数据类型的理解数组的下标法引用和指针法引用;–难点:二维数组的地址和指针概念利用字符指针存取字符串;–难点:字符数组和字符指针的区别与联系指针数组应用;–难点:指向数组的指针与指针数组的区别–带参数的main函数;动态内存分配函数及其应用;–难点:一维、二维动态数组的实现–动态数据结构(在第8章介绍)2020/3/53为什么引入指针的概念指针为函数提供修改变量值的手段指针为C的动态内存分配系统提供支持指针为动态数据结构(如例链表、队列、二叉树等)提供支持指针可以改善某些子程序的效率2020/3/54计算机内的存储部件,所有指令和数据都保存在内存内速度快,但是掉电即失可以随机访问–只要指明要访问的内存单元的地址,就可以立即访问到该单元–地址是一个无符号整数,其字长一般与主机相同–内存中的每个字节都有唯一的一个地址–地址按字节编号,按类型分配空间内存(RandomAccessMemory)地址(Address)2020/3/55寻址方式如何读写内存中的数据?–通过变量的地址访问变量所在的存储单元两种寻址方式直接(寻址)访问–直接按变量地址(或变量名)来存取变量内容的访问方式间接(寻址)访问–通过指针变量来间接存取它所指向的变量的访问方式0内存用户数据区┇20003变量i20026变量j20049变量k┇30102000变量i_pointer2020/3/56关于指针的原则学习原则–一定要学会–其实通常的应用很简单与使用变量几乎无异使用原则–永远要清楚每个指针指向的类型–永远要清楚指针指向的位置是什么2020/3/57指针(Pointer)的概念指针也是一种数据类型指针变量–具有指针类型的变量,专门存放地址数据的变量变量的指针–变量的地址2020/3/58如何定义指针变量?定义指针变量int*p;–定义了一个指针变量p,简称指针pp是变量,int*是类型–p里保存一个地址。此时这个地址是哪呢(p指向哪呢)?指针变量初始化–p=&a;–*p就像普通的变量一样使用,其值是p指向的内存的内容(在上例和a等价,但寻址方式不同)–p可以动态(任意)地指向不同内存,从而使*p代表不同的变量2020/3/59inti,*p;p=&i;int*p;float*q;p=q;inti;float*p;p=&i;int*p;p=100;指针变量只存放地址!一个指针变量不能指向与其类型不同的变量!我是真的,你猜对了吗?应在类型相同的指针变量之间赋值2020/3/510&与*操作符&用来取变量的地址–inti,*p;p=&i;–int*p,a[10];p=a;–int*p,a[10];p=&a[0];–int*p,a[10];p=&a[5];*用来存取指针所指地址单元的内容–inti,*p;p=&i;*p=0;–int*p,a[10];p=a;*p=0;–int*p,a[10];p=&a[0];*p=0;–int*p,a[10];p=&a[5];*p=0;2020/3/511指针的指向指针指向非其定义时声明的数据类型,将引起warningvoid*类型的指针可以指向任意类型的变量指针在初始化时一般int*p=NULL;–NULL表示空指针,即无效指针–但它只是逻辑上无效,并不是真正地无效如果指针指向一个非你控制的内存空间,并对该空间进行访问,将可能造成危险如int*p;*p=10;(危险的用法)2020/3/512指针变量与其它类型变量的对比共性–在内存中占据一定大小的存储单元–先定义,后使用特殊性–它的内容只能是地址,而不能是数据–必须初始化后才能使用,否则指向不确定的存储单元–只能指向同一基类型的变量–可参与的运算:加、减一个整数,自增、自减、关系、赋值2020/3/513指针运算(1/4)算术运算int*p,a[10];p=a;p++;/*p的值增加多少?*/指针的加减运算是以其指向的类型的字长为单位的6000600160026003600460056006p-1pp+12020/3/514指针运算(2/4)int*p,*q,a[10];p=a;q=&a[5];–q-p–q=p+3;指针运算不能乱算–一般只进行指针和整数的加减运算,同类型指针之间的减法运算–其它运算,比如乘法、除法、浮点运算、指针之间的加法等,并无意义,所以也不支持2020/3/515指针运算(3/4)关系运算只有指向同一种数据类型的两个指针才能进行关系运算。值为1或0–pqpqp==q指针不与非指针量进行比较,但可与NULL(即0值)进行等或不等的关系运算–判断p是否为空指针–P==NULL–p!=NULL2020/3/516指针运算(4/4)赋值运算指针在使用前一定要赋值为指针变量赋的值必须是一个地址main(){int*p;scanf(%d,p);…}main(){inta,*p=&a;scanf(%d,p);…}错!2020/3/517指针与函数指针既然是数据类型,自然可以做函数的参数和返回值的类型指针做参数的经典例子:–两数的互换2020/3/518voidSwap(int*x,int*y){inttemp;temp=*x;*x=*y;*y=temp;}main(){inta,b;a=15;b=8;Swap(&a,&b);printf(a=%d,b=%d,a,b);}voidSwap(intx,inty){inttemp;temp=x;x=y;y=temp;}main(){inta,b;a=15;b=8;Swap(a,b);printf(a=%d,b=%d,a,b);}程序1程序2例7.2:编写函数实现两数的互换主调函数被调函数实参形参结果有何不同?2020/3/519主调函数被调函数main(){inta,b;a=15;b=8;Swap(a,b);printf(a=%d,b=%d,a,b);}voidSwap(intx,inty){inttemp;temp=x;x=y;y=temp;}1515ab实参形参88xyab程序1xy2020/3/520简单变量作函数参数图7-3Swap函数调用前后参数变化的示意图1581581581515881581515a)调用Swap函数(b)执行Swap函数c)从Swap函数返回tempxybmain函数Swap函数①②③aaatemptempxxyybb2020/3/521主调函数被调函数main(){inta,b;a=15;b=8;Swap(&a,&b);printf(a=%d,b=%d,a,b);}voidSwap(int*x,int*y){inttemp;temp=*x;*x=*y;*y=temp;}&a&a实参形参&b&b*x*yab程序2xy15ab82020/3/522指针变量作函数参数图7-5用指针变量作函数参数实现两数互换函数的示意图158815&a&b&a&b15(a)调用Swap函数(b)执行Swap函数aabb*x*x*y*yyyxx&a&bmain函数Swap函数temptemp②①③2020/3/523swap函数的几种错误形式(1/3)参数单向传递voidSwap(intx,inty){inttemp;temp=x;/*x,y为内部变量*/x=y;y=temp;}2020/3/524swap函数的几种错误形式(2/3)参数单向传递voidSwap(int*p1,int*p2){int*p;p=p1;/*p1,p2为内部变量*/p1=p2;p2=p;}2020/3/525swap函数的几种错误形式(3/3)指针p没有确切地址voidSwap(int*p1,int*p2){int*p;/*指针p未初始化*/*p=*p1;*p1=*p2;*p2=*p;}2020/3/526例7.3:打印出最高分及其学号voidFindMax(floatscore[],longnum[],intn,float*pMaxScore,long*pMaxNum){inti;*pMaxScore=score[0];*pMaxNum=num[0];for(i=1;in;i++){if(score[i]*pMaxScore){*pMaxScore=score[i];*pMaxNum=num[i];}}}2020/3/527例7.4:日期转换问题任意给定某年某月某日,打印出它是这一年的第几天已知某一年的第几天,计算它是该年的第几月第几日staticintdayTab[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},{0,31,29,31,30,31,30,31,31,30,31,30,31}};2020/3/528计算某年某月某日是这一年的第几天/*函数功能:对给定的某年某月某日,计算出它是这一年的第几天函数参数:整型变量year、month、day,分别代表年、月、日函数返回值:这一年的第几天*/intDayofYear(intyear,intmonth,intday){inti,leap;/*若year为闰年,则leap值为1,就用第2行元素dayTab[1][i]计算,否则leap值为0,用第1行dayTab[0][i]计算*/leap=((year%4==0)&&(year%100!=0))||(year%400==0);for(i=1;imonth;i++){day=day+dayTab[leap][i];}returnday;/*返回计算出的day的值*/}2020/3/529计算某年的第几天是该年的第几月第几日/*函数功能:对给定的某一年的第几天,计算它是这一年的第几月第几日函数入口参数:整型变量year,存储年整型变量yearDay,存储这一年的第几天函数出口参数:整型指针pMonth,指向存储这一年第几月的整型变量整型指针pDay,指向存储第几日的整型变量函数返回值:无*/voidMonthDay(intyear,intyearDay,int*pMonth,int*pDay){inti,leap;leap=((year%4==0)&&(year%100!=0))||(year%400==0);for(i=1;yearDaydayTab[leap][i];i++){yearDay=yearDay-dayTab[leap][i];}*pMonth=i;/*将计算出的月份值赋值给pMonth所指向的变量*/*pDay=yearDay;/*将计算出的日号赋值给pDay所指向的变量*/}2020/3/530字符串(String)与字符数组、字符指针字符串–一串以'\0'结尾的字符在C语言中被看作字符串–用双引号括起的一串字符是字符串常量,C语言自动为其添加'\0'终结符–C语言并没有为字符串提供任何专门的表示法,完全使用字符数组和字符指针来处理字符数组–每个元素都是字符类型的数组charstring[100];字符指针–指向字符类型的指针char*p;数组和指针可以等同看待,上面三者本质上是一回事2020/3/531字符指针变量与字符数组的区别定义方法不同charstr[10];char*ptr;赋值方法和含义不同charstr[10];str=”china”;/*错误*/strcpy(str,”china”);/*正确*/char*ptr;ptr=”china”;2020/3/532字符指针变量与字符数组的区别在定义一个数组时,在编译时即分配单元,有确定地址,而定义一个字符指针变量时,如未对它赋初值,则其所指数据是不定的,因而使用是危险的。例如,输入字符串时charstr[10];scanf(%s,str);/*正确*/char*a;scanf(%s,a);/*错误*/应为:char*a;charstr[10];a=str;s